在开始学习Thread以前,咱们先来了解一下 线程和进程之间的关系:html
线程(Thread)是进程的一个实体,是CPU调度和分派的基本单位。 线程不可以独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。java
由上描述,能够得知线程做为cpu的基本调度单位,只有把多线程用好,才能充分利用cpu的多核资源。git
本文基于JDK 8(也能够叫JDK 1.8)。github
建立线程有四种方式:api
public class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
复制代码
Thread thread = new Thread(new MyThread());
thread.start();
复制代码
public class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
复制代码
MyThread thread = new MyThread();
thread.start();
复制代码
以上代码有更简单的写法,以下:多线程
Thread thread = new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
thread.start();
复制代码
new Thread(()-> System.out.println(Thread.currentThread().getName())).start();
复制代码
看源码能够知道Thread的父类是Runnable是JDK1.0提供的,而Callable和Runnable相似,是JDK1.5提供的,弥补了调用线程没有返回值的状况,能够看作是Runnable的一个补充,下面看看Callable的实现。oracle
public class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName());
return Thread.currentThread().getName();
}
}
复制代码
Callable<String> callable = new MyThread();
FutureTask<String> ft = new FutureTask<>(callable);
new Thread(ft,"threadName").start();
System.out.println(ft.get());
复制代码
真正启动线程的是start()方法而不是run(),run()和普通的成员方法同样,能够重复使用,但不能启动一个新线程。ide
Thread类方法学习
方法 | 说明 |
---|---|
start() | 启动线程 |
setName(String name) | 设置线程名称 |
setPriority(int priority) | 设置线程优先级,默认5,取值1-10 |
join(long millisec) | 挂起线程xx毫秒,参数能够不传 |
interrupt() | 终止线程 |
isAlive() | 测试线程是否处于活动状态 |
Thread静态(static)方法测试
方法 | 说明 |
---|---|
yield() | 暂停当前正在执行的线程对象,并执行其余线程。 |
sleep(long millisec)/sleep(long millis, int nanos) | 挂起线程xx秒,参数不可省略 |
currentThread() | 返回对当前正在执行的线程对象的引用 |
holdsLock(Object x) | 当前线程是否拥有锁 |
sleep为线程的方法,而wait为Object的方法,他们的功能类似,最大本质的区别是:sleep不释放锁,wait释放锁。
用法上的不一样:sleep(milliseconds)能够用时间指定来使他自动醒过来,若是时间不到你只能调用interreput()来终止线程;wait()能够用notify()/notifyAll()直接唤起。
重点: 测试wait和sleep释放锁的代码以下:
public class SynchronizedTest extends Thread {
int number = 10;
public synchronized void first(){
System.out.println("this is first!");
number = number+1;
}
public synchronized void secord() throws InterruptedException {
System.out.println("this is secord!!");
Thread.sleep(1000);
// this.wait(1000);
number = number*100;
}
@Override
public void run() {
first();
}
}
复制代码
SynchronizedTest synchronizedTest = new SynchronizedTest();
synchronizedTest.start();
synchronizedTest.secord();
// 主线程稍等10毫秒
Thread.sleep(10);
System.out.println(synchronizedTest.number);
复制代码
根据结果能够得知:
总结: 使用 sleep(1000)不释放同步锁,执行的是10*100+1=1001,wait(1000)释放了锁,执行的顺序是(10+1)x100=1100,因此sleep不释放锁,wait释放锁。
线程状态:
线程的状态可使用getState()查看,更多状态详情,查看Thread源码,以下图:
Thread thread = new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
// 只声明不调用start()方法,获得的状态是NEW
System.out.println(thread.getState()); // NEW
复制代码
Thread thread = new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
thread.start();
System.out.println(thread.getState()); // RUNNABLE
复制代码
使用synchronized同步阻塞实现,代码以下:
public class MyCounter {
int counter;
public synchronized void increase() {
counter++;
try {
Thread.sleep(10*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
复制代码
MyCounter myCounter = new MyCounter();
// 线程1调用同步线程,模拟阻塞
new Thread(()-> myCounter.increase()).start();
// 线程2继续调用同步阻塞方法
Thread thread = new Thread(()-> myCounter.increase());
thread.start();
// 让主线程等10毫秒
Thread.currentThread().sleep(10);
// 打印线程2,为阻塞状态:BLOCKED
System.out.println(thread.getState());
复制代码
public class MyThread extends Thread{
@Override
public void run() {
synchronized (MyThread.class){
try {
MyThread.class.wait();
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
复制代码
Thread thread = new Thread(new MyThread());
thread.start();
// 主线程挂起200毫秒,等thread执行完成
Thread.sleep(200);
// 输出WAITING,线程thread一直处于被挂起状态
System.out.println(thread.getState());
复制代码
唤醒线程: 可以使用 notify/notifyAll 方法,代码以下:
synchronized (MyThread.class) {
MyThread.class.notify();
}
复制代码
使线程WAITING的方法:
查看Thread源码能够知道Thread的join方法,底层使用的是Object的wait实现的,以下图:
注意: 查看Object的源码可知wait(),不传递参数,等同于wait(0),设置的“0”不是当即执行,而是无限的等待,不执行,以下图:
TIMED_WAITING状态,只须要给wait设置上时间便可,代码以下:
public class MyThread extends Thread{
@Override
public void run() {
synchronized (MyThread.class){
try {
MyThread.class.wait(1000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
复制代码
调用代码仍是同样的,以下:
Thread thread = new Thread(new MyThread());
thread.start();
// 主线程挂起200毫秒,等thread执行完成
Thread.sleep(200);
// 输出TIMED_WAITING
System.out.println(thread.getState());
synchronized (MyThread.class) {
MyThread.class.notify();
}
复制代码
Thread thread = new Thread(()-> System.out.println(Thread.currentThread().getName()));
thread.start();
// 让主线程等10毫秒
Thread.currentThread().sleep(10);
System.out.println(thread.getState());
复制代码
根据前面的知识,咱们知道使用sleep的时候是不释放锁的,因此利用这个特性咱们能够很轻易的写出死锁的代码,具体的流程如图(图片来源于杨晓峰老师文章):
代码以下:
static Object object1 = new Object();
static Object object2 = new Object();
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
synchronized (object1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object2){
System.out.println(Thread.currentThread().getName());
}
}
}
};
Thread thread2 = new Thread(){
@Override
public void run() {
synchronized (object2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object1){
System.out.println(Thread.currentThread().getName());
}
}
}
};
thread.start();
thread2.start();
复制代码
运行上面的代码,程序会处于无限等待之中。
根据上面的内容,咱们已经系统的学习Thread的使用了,然而学而不思则罔,最后留一个思考题:根据本文介绍的知识,怎么能避免死锁?(哈哈,卖个关子,根据文章的知识点组合能够得出答案)
参考文档