JUC笔记(一):线程基础
JUC笔记(一):线程基础
创建线程
方法一:直接使用Thread
1 | Thead t = new Tread() { |
方法二:使用runnable
1 | Runnable runnable = new Runnable() { |
简单的源码解析
1 | public Thread(Runnable target) { |
其实就是将runnable传入Thread中的时候赋值个target,当Thread调用run方法的时候就会调用target的run方法。
方法三:通过FutureTask
1 | Callable<Integer> callable = new Callable<Integer>() { |
- FutrueTask可以通过get获取线程方法执行的结果,阻塞。
常见方法
方法 | 作用 |
---|---|
join() | 等待线程运行结束 |
join(long n) | 等待线程运行结束,最多等待n毫秒 |
getId() | 获取线程长整型id |
getName(String name) | 获取线程名称 |
getPriority() | 获取线程优先级 |
setPriority(int) | 修改线程优先级 |
getState() | 获取线程状态 |
isInterrupted() | 判断是否被打断(不会清除打断标记) |
isAlive() | 线程是否存活(还没有运行完毕) |
interrupt() | 打断线程 |
interrupted() | 判断当前线程是否被打断(会清除打断标记) |
currentThread() | 获取当前正在执行的线程 |
sleep(long n) | 让当前执行的线程休眠n毫秒,休眠时让出时间片 |
yield() | 提示线程调度器让出当前线程对CPU的使用 |
sleep和yield
sleep
让当前线程从Running进入Timed Waiting状态
其他线程可以使用interrupt方法打断正在睡眠的线程,这时sleep方法会抛出InterruptedException
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17Thread t1 = new Thread("t1") {
public void run() {
System.out.println("enter sleep...");
try{
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("wake up...");
throw new RuntimeException(e);
}
}
};
t1.start();
Thread.sleep(1000);
//唤醒t1
System.out.println("interrupt...");
t1.interrupt();休眠结束以后的线程未必会立即得到执行。
yield
- 调用yield会让当前线程从Running进入Runnable状态,然后调度执行其他线程。
线程优先级
最小优先级
MIN_PRIORITY = 1
,默认优先级NORM_PRIORITY = 5
,最大优先级MAX_PRIORITY = 10
线程优先即会提示(hint)调度器优先调度该线程,但是他仅仅是一个提示,调度器可以忽略它
如果CPU比较忙,那么优先级高的线程会获得更多的时间片,如果CPU空闲,那么优先级基本没作用
1 | Thread t1 = new Thread(()->{ |
好像没什么区别。不知为哈,设置了优先级,t2并没有运行得更多:disappointed_relieved:
join
用于等待一个线程执行结束。想象一个场景,主线程需要获取子线程的执行结果。如果不做特殊操作,主线程不会等待子线程执行,而是直接执行结束。使用sleep方法也不行,因为主线程并不知道子线程何时执行结束。这个时候就可以尝试使用join
1 | static int r = 0; |
interrupt
打断sleep,wait,join的线程,打断这些正在阻塞的线程,当然也可以打断正在运行的线程。
打断sleep,wait,join的线程
1 | //try一try |
会有一个打断标记,如果一个线程被打断的话,就会有被设置一个打断标记
- 对sleep,wait,join的线程(以异常的方式表示被打断)被打断以后,打断标记会被清除
打断正常运行的线程
会设置打断标记,至于是否要打断,由被打断的线程决定
1 | Thread t1 = new Thread(()->{ |
设计模式-两阶段终止
使用stop()方法停止线程的危害
- stop方法会真正杀死线程,如果这时线程锁住了共享资源,那么它被杀死以后就没机会释放锁了。
两阶段终止(下面是一个监控线程的例子)
1 | private Thread monitor; |
打断park
线程执行LockSupport.park();
以后会停止运行。调用线程的interrupt()
可以打断处于park的线程,让线程继续执行。
1 | Thread t1 = new Thread(()->{ |
线程状态
从操作系统层面(五种)
- 初始状态:仅仅在语言层面创建了线程对象,还未与操作系统关联
- 可运行状态(就绪状态):指线程已经被创建,并在就绪队列里,等待CPU调度
- 运行状态:获取了CPU时间片,正在运行
- 阻塞状态:调用了阻塞的API,使得线程处于阻塞状态
- 终止状态:线程已经执行完毕,生命周期已经结束
从java层面(六种)
new:线程刚被创建,没有start
runnable:
- 请求IO等操作使得java线程处于阻塞状态时,在java层面也叫RUNNABLE
- 运行状态
- 可运行状态在java层面也时RUNNABLE
terminated:结束状态
blocked:等待锁导致了阻塞
time_waiting:通过待用join或sleep等有时限的等待
waiting:调用join等无时限制的等待