思维导图在此:
1、线程与协程
协程-->线程-->进程,协程最小
协程:用户态,go语言
线程:用户态、内核态都有。cpu调度的最小单位。是工人,从进程获取资源,多个线程共享进程的资源。
进程:内核态。操作系统调度资源的最小单位。是资源管家。
2、调度机制
协同式。线程的切换由线程控制。A线程执行完再执行线程B。好处是没有线程同步问题,坏处是A出问题,后面会阻塞。
抢占式。线程的切换由操作系统控制。好处是不会阻塞,坏处是AB不一定谁先执行,且A可能还没执行完就切换到B了,有上下文转换。
java是抢占式,Thread.yeild()可以释放时间片,有上下文切换
3、如何创建线程
start() 和 run()
// start是thread的方法,创建了线程,并执行runnable任务
new Thread(runnable).start();
// run是Runnable的方法,没有创建线程,只执行了任务
new Thread(() -> System.out.println("只是一个lambda表达式创建的Runnable")).run();
4、如何创建任务
Runnable无返回值,不抛出异常
Callable有返回值,抛出异常
Callable底层是Runnable
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("只是一个Runnable");
}
};
Runnable runnable1 = ()->System.out.println("只是一个lambda表达式创建的Runnable");
Callable callable = new Callable() {
@Override
public Object call() throws Exception {
return "这是一个callable";
}
};
5、java线程本质
java线程绑定jvm的线程(JVM Java Thread),jvm线程绑定操作系统的线程(OS Thread)。然后操作系统线程的状态与jvm线程的状态进行映射绑定
6、线程的几个方法
6.1、想让线程稍等一下
sleep(x):释放时间片(操作系统)、不释放锁(锁与线程无关)。x毫秒后开始竞争时间片
(注意:wait()是Object的方法,释放时间片,释放锁。锁和Object有关,Object中有记录所状态的字段)
yield():就是 sleep(0)
6.2、想让线程执行完再执行当前线程
join():main线程里写了A.join()代码。表示A先参加,即main会阻塞,等A线程执行完,再走main线程
6.3、想让线程停止
stop():释放时间片(马上停止线程),后面的代码不执行,不优雅,不推荐使用
interrupt():“中断”。更改状态,不释放时间片(不会停线程),中断标志位设为true
isInterrupted():“是否中断”。判断中断标志位是否是true
interrupted():“结束中断”。判断中断标志位是否是true,如果是true需要改为false(擦除)
6.4、如何优雅中断线程
-
设置中断状态:
使用Thread.interrupt()
方法来设置线程的中断状态。这并不会立即停止线程,而是设置了一个中断请求标志。 -
检查中断状态:
被中断的线程应该周期性地检查其中断状态,通常通过Thread.currentThread().isInterrupted()
方法。 -
响应中断:
当线程检测到中断请求时,它应该适当地响应。这通常意味着清理资源、保存状态,并优雅地终止。 -
不要使用
Thread.stop()
:Thread.stop()
方法已经被废弃,因为它可能导致线程留下不一致的状态。应该总是使用中断和协作机制来停止线程。 -
处理阻塞操作:
当线程在阻塞操作(如sleep()
,wait()
,join()
, 或者某些I/O操作)上被中断时,这些操作会抛出InterruptedException
。捕获这个异常,清除中断状态(如果需要),并适当地响应中断。 -
传递中断:
如果线程在调用其他可中断的阻塞方法时检测到中断,它应该清除自己的中断状态,并设置被调用线程的中断状态,以便后者也能响应中断。
7、线程通信
7.1、两个线程读取同一个变量
volatile:
Java中的volatile_java volatile-CSDN博客
底层:JMM、内存屏障、总线监听与缓存一致性协议
可见性:见到最新的数据。读时工作内存失效,获取主内存。写时工作内存马上刷新到主内存
有序性:和代码书写的顺序一样。禁止cpu优化指令重排。volatile写之前的读写操作不能重排在volatile写之后(和代码逻辑顺序一样)。volatile读之后的读写操作不能重排到volatile读之前(和代码逻辑顺序一样)。
管道:
用的不多
输入流和输出流进行绑定,形成管道。PipedWriter和PipedReader
B里创建管道变量
A = new thread(管道放进去)
B里操作管道变量,A里的管道变量会跟着变,这就实现了AB两个线程的通信,变量共享了,和volatile有点像
7.2、线程之间的阻塞和唤醒
wait()和notify()。Object的方法。涉及到锁,要在synchronize里面用wait,notify唤醒线程。
join() 、sleelp() 。Thread的方法,底层是wait(),notify()