概述
一、概述
在开始学习Thread之前,我们先来了解一下 线程和进程之间的关系:
线程(Thread)是进程的一个实体,是CPU调度和分派的基本单位。 线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。
由上描述,可以得知线程作为cpu的基本调度单位,只有把多线程用好,才能充分利用cpu的多核资源。
本文基于JDK 8(也可以叫JDK 1.8)。
二、线程使用
2.1 启动线程
创建线程有四种方式:
- 实现Runnable接口
- 继承Thread类
- 使用JDK 8 的Lambda
- 使用Callable和Future
2.1.1 Runnable创建方式
1 2 3 4 5 6 | public class MyThread implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); } } |
1 2 | Thread thread = new Thread(new MyThread()); thread.start(); |
2.1.2 继承Thread创建方式
1 2 3 4 5 6 | public class MyThread extends Thread{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } } |
1 2 | MyThread thread = new MyThread(); thread.start(); |
以上代码有更简单的写法,如下:
1 2 3 4 5 6 7 | Thread thread = new Thread(){ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }; thread.start(); |
2.1.3 Lambda创建方式
1 | new Thread(()-> System.out.println(Thread.currentThread().getName())).start(); |
2.1.4 使用Callable和Future
看源码可以知道Thread的父类是Runnable是JDK1.0提供的,而Callable和Runnable类似,是JDK1.5提供的,弥补了调用线程没有返回值的情况,可以看做是Runnable的一个补充,下面看看Callable的实现。
1 2 3 4 5 6 7 8 | public class MyThread implements Callable<String> { @Override public String call() throws Exception { System.out.println(Thread.currentThread().getName()); return Thread.currentThread().getName(); } } |
1 2 3 4 | Callable<String> callable = new MyThread(); FutureTask<String> ft = new FutureTask<>(callable); new Thread(ft,"threadName").start(); System.out.println(ft.get()); |
2.1.5 run()和start()的区别
真正启动线程的是start()方法而不是run(),run()和普通的成员方法一样,可以重复使用,但不能启动一个新线程。
2.2 Thread的常用方法
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) | 当前线程是否拥有锁 |
2.3 sleep()和wait()的区别
sleep为线程的方法,而wait为Object的方法,他们的功能相似,最大本质的区别是:sleep不释放锁,wait释放锁。
用法上的不同:sleep(milliseconds)可以用时间指定来使他自动醒过来,如果时间不到你只能调用interreput()来终止线程;wait()可以用notify()/notifyAll()直接唤起。
重点: 测试wait和sleep释放锁的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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(); } } |
1 2 3 4 5 6 | SynchronizedTest synchronizedTest = new SynchronizedTest(); synchronizedTest.start(); synchronizedTest.secord(); // 主线程稍等10毫秒 Thread.sleep(10); System.out.println(synchronizedTest.number); |
根据结果可以得知:
- 执行sleep(1000)运行的结果是:1001
- 执行wait(1000)运行的结果是:1100
总结: 使用 sleep(1000)不释放同步锁,执行的是10*100+1=1001,wait(1000)释放了锁,执行的顺序是(10+1)x100=1100,所以sleep不释放锁,wait释放锁。
三、线程状态
3.1 线程状态概览
线程状态:
- NEW 尚未启动
- RUNNABLE 正在执行中
- BLOCKED 阻塞的(被同步锁或者IO锁阻塞)
- WAITING 永久等待状态
- TIMED_WAITING 等待指定的时间重新被唤醒的状态
- TERMINATED 执行完成
线程的状态可以使用getState()查看,更多状态详情,查看Thread源码,如下图:
3.2 线程的状态代码实现
3.2.1 NEW 尚未启动状态
1 2 3 4 5 6 7 8 | Thread thread = new Thread() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }; // 只声明不调用start()方法,得到的状态是NEW System.out.println(thread.getState()); // NEW |
3.2.2 RUNNABLE 运行状态
1 2 3 4 5 6 7 8 | Thread thread = new Thread() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }; thread.start(); System.out.println(thread.getState()); // RUNNABLE |
3.2.3 BLOCKED 阻塞状态
使用synchronized同步阻塞实现,代码如下:
1 2 3 4 5 6 7 8 9 10 11 | public class MyCounter { int counter; public synchronized void increase() { counter++; try { Thread.sleep(10*1000); } catch (InterruptedException e) { e.printStackTrace(); } } } |
1 2 3 4 5 6 7 8 9 10 11 | 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()); |
3.2.4 WAITING 永久等待状态
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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(); } } } } |
1 2 3 4 5 6 | Thread thread = new Thread(new MyThread()); thread.start(); // 主线程挂起200毫秒,等thread执行完成 Thread.sleep(200); // 输出WAITING,线程thread一直处于被挂起状态 System.out.println(thread.getState()); |
唤醒线程: 可使用 notify/notifyAll 方法,代码如下:
1 2 3 | synchronized (MyThread.class) { MyThread.class.notify(); } |
使线程WAITING的方法:
- Object的wait() 不设置超时时间
- Thread.join()不设置超时时间
- LockSupport的park()
查看Thread源码可以知道Thread的join方法,底层使用的是Object的wait实现的,如下图:
注意: 查看Object的源码可知wait(),不传递参数,等同于wait(0),设置的“0”不是立即执行,而是无限的等待,不执行,如下图:
3.2.5 TIMED_WAITING 超时等待状态
TIMED_WAITING状态,只需要给wait设置上时间即可,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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(); } } } } |
调用代码还是一样的,如下:
1 2 3 4 5 6 7 8 9 | 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(); } |
3.2.6 TERMINATED 完成状态
1 2 3 4 5 | Thread thread = new Thread(()-> System.out.println(Thread.currentThread().getName())); thread.start(); // 让主线程等10毫秒 Thread.currentThread().sleep(10); System.out.println(thread.getState()); |
四、死锁
根据前面的知识,我们知道使用sleep的时候是不释放锁的,所以利用这个特性我们可以很轻易的写出死锁的代码,具体的流程如图(图片来源于杨晓峰老师文章):
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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(); |
运行上面的代码,程序会处于无限等待之中。
最后
以上就是温婉冥王星为你收集整理的Java核心(一)Thread详解一、概述二、线程使用三、线程状态四、死锁的全部内容,希望文章能够帮你解决Java核心(一)Thread详解一、概述二、线程使用三、线程状态四、死锁所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复