Java并发
2020-12-25 16:06:11 0 举报
AI智能生成
Java并发思维导图
作者其他创作
大纲/内容
基础线程机制
Executor
Executor 管理多个异步任务的执行,这里的异步是指多个任务的执行互不干扰,不需要进行同步操作
Executor 管理多个异步任务的执行,这里的异步是指多个任务的执行互不干扰,不需要进行同步操作
CachedThreadPool :一个任务创建一个线程
FixedThreadPool : 所有任务只能使用固定大小的线程
SingleThreadExecutor : 相当于大小为1的FixedThreadPool
Daemon
守护线程是程序运行时在后台提供服务的线程,不属于程序中不可或缺的部分
当所有非守护线程结束,程序也就终止,同时会杀死所有守护线程
main()属于非守护线程
守护线程是程序运行时在后台提供服务的线程,不属于程序中不可或缺的部分
当所有非守护线程结束,程序也就终止,同时会杀死所有守护线程
main()属于非守护线程
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.setDaemon(true);
}
Thread thread = new Thread(new MyRunnable());
thread.setDaemon(true);
}
sleep()
Thread.sleep(millisec) 方法会休眠当前正在执行的线程,millisec 单位为毫秒
sleep()可能会抛出InterruptException, 因为异常不能跨线程传播灰main() 中,
因此必须在本地进行处理,线程中抛出的其它异常也同样需要在本地进行处理.
Thread.sleep(millisec) 方法会休眠当前正在执行的线程,millisec 单位为毫秒
sleep()可能会抛出InterruptException, 因为异常不能跨线程传播灰main() 中,
因此必须在本地进行处理,线程中抛出的其它异常也同样需要在本地进行处理.
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
yield()
对静态方法Thread.yield() 的调用声明了当前线程已经完成了生命周期中最重要的部分
可以切换给其它线程来执行,该方法只是对线程调度器的一个建议,而且也只是建议具有
优先级的其它线程可以运行
对静态方法Thread.yield() 的调用声明了当前线程已经完成了生命周期中最重要的部分
可以切换给其它线程来执行,该方法只是对线程调度器的一个建议,而且也只是建议具有
优先级的其它线程可以运行
public void run() {
Thread.yield();
}
Thread.yield();
}
互斥同步
Java提供了两种锁机制来控制多个线程对共享资源的互斥访问, 第一个是JVM 实现的synchronize ,
而另一个是JDK实现的ReentrantLock
而另一个是JDK实现的ReentrantLock
synchronize
同步代码块
public void func() {
synchronized (this) {
// ...
}
}
synchronized (this) {
// ...
}
}
它只作用于同一个对象,如果调用;两个对象上的同步代码块,就不会进行同步
同步一个方法
(作用于用一个对象)
(作用于用一个对象)
public synchronized void func () {
// ...
}
// ...
}
同步类
作用于整个类, 也就是说两个线程调用同一个类的不同对象上的这种同步语句,也会进行同步
作用于整个类, 也就是说两个线程调用同一个类的不同对象上的这种同步语句,也会进行同步
public class SynchronizedExample {
public void func2() {
synchronized (SynchronizedExample.class) {
for (int i = 0; i < 10; i++) {
System.out.print(i + " ");
}
}
}
}
public void func2() {
synchronized (SynchronizedExample.class) {
for (int i = 0; i < 10; i++) {
System.out.print(i + " ");
}
}
}
}
同步一个静态方法
作用于整个类
作用于整个类
public synchronized static void fun() {
// ...
}
// ...
}
ReentrantLock
实现了Lock接口,但需要自己释放锁
实现了Lock接口,但需要自己释放锁
比较
锁的实现
synchronize 是JVM 实现的, 而 ReentrantLock 是JDK 实现的
性能
新版本Java 对 synchronize 进行了很多优化, 例如自旋锁等, synchronize 和 ReentrantLock 大致相同
等待可中断
当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其它事情, ReentrantLock可以中断,但synchronize不行
公平锁
公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁
synchronize 中的锁是非公平的, ReentrantLock默认情况下也是非公平的,但是也可以是公平的
synchronize 中的锁是非公平的, ReentrantLock默认情况下也是非公平的,但是也可以是公平的
锁绑定多个条件
一个ReentrantLock可以同时绑定多个condition 对象
使用选择
除非需要使用ReentrantLock 的高级功能,否则优先使用synchronize ,这是因为synchronize 是JVM实现的一种锁机制
JVM原生的支持它, 而ReentrantLock不是所有的JDK 版本都支持,并使用synchronize 不用担心没有释放锁而导致死锁问题
因为JVM会确保锁的释放
JVM原生的支持它, 而ReentrantLock不是所有的JDK 版本都支持,并使用synchronize 不用担心没有释放锁而导致死锁问题
因为JVM会确保锁的释放
线程状态
新建(NEW)
创建后尚未启动
可运行(RUNABLE)
正在Java虚拟机中运行,但是在操作系统层面,它可能处于运行状态,也可能等待资源调度
资源调度完成就进入运行状态,所以该状态的可运行是指可以被运行,具体有没有运行要看
底层操作系统的资源调度
资源调度完成就进入运行状态,所以该状态的可运行是指可以被运行,具体有没有运行要看
底层操作系统的资源调度
阻塞(BLOCKED)
请求获取monitor lock 从而进入synchronize 函数或者代码块,但是其它线程已经占用了该monitor lock
所以出于阻塞状态,要结束该状态进入从而RUNABLE 需要其它线程释放monitor lock
所以出于阻塞状态,要结束该状态进入从而RUNABLE 需要其它线程释放monitor lock
无限期等待(WAITING)
等待其它线程显地唤醒
阻塞和等待的区别在于,阻塞是被动的,它是在等待获取线程锁.
而等待是主动的,通过调用Object.wait() 等方法进入.
阻塞和等待的区别在于,阻塞是被动的,它是在等待获取线程锁.
而等待是主动的,通过调用Object.wait() 等方法进入.
进入方法 退出方法
没有设置 Timeout 参数的 Object.wait() 方法 Object.notify() / Object.notifyAll()
没有设置 Timeout 参数的 Thread.join() 方法 被调用的线程执行完毕
LockSupport.park() 方法 LockSupport.unpark(Thread)
没有设置 Timeout 参数的 Object.wait() 方法 Object.notify() / Object.notifyAll()
没有设置 Timeout 参数的 Thread.join() 方法 被调用的线程执行完毕
LockSupport.park() 方法 LockSupport.unpark(Thread)
限期等待(TIMED_WAITING)
无需等待其他线程显式地唤醒,在一定时间之后会被系统自动唤醒
进入方法 退出方法
Thread.sleep() 方法 时间结束
设置了 Timeout 参数的 Object.wait() 方法 时间结束 / Object.notify() / Object.notifyAll()
设置了 Timeout 参数的 Thread.join() 方法 时间结束 / 被调用的线程执行完毕
LockSupport.parkNanos() 方法 LockSupport.unpark(Thread)
LockSupport.parkUntil() 方法 LockSupport.unpark(Thread)
Thread.sleep() 方法 时间结束
设置了 Timeout 参数的 Object.wait() 方法 时间结束 / Object.notify() / Object.notifyAll()
设置了 Timeout 参数的 Thread.join() 方法 时间结束 / 被调用的线程执行完毕
LockSupport.parkNanos() 方法 LockSupport.unpark(Thread)
LockSupport.parkUntil() 方法 LockSupport.unpark(Thread)
死亡(TERMINATED)
可以是线程结束任务之后自己结束,或者产生了异常而结束
Java内存模型
主内存与工作内存
处理器上的寄存器的读写的速度比内存快几个数量级,为了解决这种速度矛盾,
在它们之间加入了高速缓存
在它们之间加入了高速缓存
加入高速缓存带来了一个新的问题:缓存一致性.如果多个缓存共享同一块主内存区域
那么多个缓存的数据可能会不一致,需要一些协议来解决这个问题
那么多个缓存的数据可能会不一致,需要一些协议来解决这个问题
所有的变量都存储在主内存中,每一个线程还有自己的工作内存
工作内存存储在高速缓存或者寄存器中,保存了该线程使用的变量的
主内存副本拷贝
工作内存存储在高速缓存或者寄存器中,保存了该线程使用的变量的
主内存副本拷贝
线程只能直接操作工作内存中的变量, 不同线程之前的变量值传递需要通过主内存来完成
内存间交互操作
read
把一个变量的值从主内存传输到工作内存中
load
在read之后执行,把read得到的值放入工作内存的变量副本中
use
把工作内存中一个变量的值传递给执行引擎
assign
把一个从执行引擎接收的值赋给工作内存的变量
store
把工作内存的一个变量的值传送到主内存中
write
在store之后执行,把store得到的值放入主内存的变量中
lock
作用于主内存的变量
unlock
内存模型三大特征
原子性
java内存模型保证了内存间交互操作时具有原子性
可见性
可见性指当一个线程修改了共享变量的值,其它线程能够立即得知这个修改
java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主
内存刷新变量值来实现可见性的.
java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主
内存刷新变量值来实现可见性的.
实现方式
volatile (关键字修饰)
synchronize ,对一个变量执行unlock 操作之前,必须把变量值同步回主内存
final ,被final 关键字修饰的字段在构造器中一旦初始化完成
并且没有发生this逃逸(其它线程通过this引用访问到初始化
一半的对象),那么其它线程就能看见final字段的值
并且没有发生this逃逸(其它线程通过this引用访问到初始化
一半的对象),那么其它线程就能看见final字段的值
有序性
在本线程内观察,所有操作都是有序的,在一个线程观察另一个线程
所有操作都是无序的,无序是因为发生了指令重排序,在java内存模
型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响
到单线程程序的执行,却会影响到多线程并发执行的正确性
所有操作都是无序的,无序是因为发生了指令重排序,在java内存模
型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响
到单线程程序的执行,却会影响到多线程并发执行的正确性
volatile 关键字通过添加内存屏障的方式来禁止指令重排
即重排序时不能把后面的指令放在内存屏障之前
即重排序时不能把后面的指令放在内存屏障之前
也可以通过synchronize 来保证有序性,它保证每个时刻只有一个线程执行同步代码
相当于是让线程顺序执行同步代码
相当于是让线程顺序执行同步代码
先行发生原则
单一线程原则
在一个线程内,在程序前面的操作先行发生于后面的操作
管程锁定规则
一个unlock操作先行发生于后面对同一个锁的lock操作
volatile 变量规则
对一个volatile变量的写操作先行发生于后面对这个变量的读操作
线程启动规则
Thread对象的start()方法调用先行发生于此线程的每一个动作
线程加入规则
Thread对象的结束先行发生于join()方法返回
线程中断规则
对线程interrupt()方法的调用先行发生于中断线程的代码检测到中断事件的发生
可以通过Interrupt() 方法检测到是否有中断发生
可以通过Interrupt() 方法检测到是否有中断发生
对象终结规则
一个对象的初始化完成(构造函数执行结束)先行于他的finalize() 方法的开始
传递性
如果操作A先行发生于操作B ,操作B先行发生于操作C,那么操作A先行发生于操作C
使用线程
实现Runnable接口
需要实现接口中的run() 方法 使用Runnable 实例再创建一个Thread,
然后调用Thread实例的start()方法来启动线程
然后调用Thread实例的start()方法来启动线程
public class MyRunnable implements Runnable {
@Override
public void run() {
// ...
}
}
public static void main(String[] args) {
MyRunnable instance = new MyRunnable();
Thread thread = new Thread(instance);
thread.start();
}
@Override
public void run() {
// ...
}
}
public static void main(String[] args) {
MyRunnable instance = new MyRunnable();
Thread thread = new Thread(instance);
thread.start();
}
实现Callable接口
与Runnable相比, Callable 可以有返回值, 返回值通过FutureTask 进行封装
public class MyCallable implements Callable<Integer> {
public Integer call() {
return 123;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable mc = new MyCallable();
FutureTask<Integer> ft = new FutureTask<>(mc);
Thread thread = new Thread(ft);
thread.start();
System.out.println(ft.get());
}
public Integer call() {
return 123;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable mc = new MyCallable();
FutureTask<Integer> ft = new FutureTask<>(mc);
Thread thread = new Thread(ft);
thread.start();
System.out.println(ft.get());
}
继承Thread类
同样也是需要实现run()方法,因为Thread类也实现了Runnable接口
public class MyThread extends Thread {
public void run() {
// ...
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
}
public void run() {
// ...
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
}
实现接口 VS 继承Thread
Java不支持多重继承,因此继承了Thread类就无法继承其它的类,但是可以实现多个接口
类可能只要求可执行就行,继承整个Thread 类开销过大
中断
一个线程执行完毕之后会自动结束,如果再运行过程中发生异常也会提前结束
InterruptedException
通过调用一个线程interrupt() 来中断该线程,如果该线程处于阻塞,限期等待或者无限期等待状态
那么就会抛出InterruptedException.从而提前结束该线程,但是不能中断 I / O阻塞和synchronize
锁阻塞
那么就会抛出InterruptedException.从而提前结束该线程,但是不能中断 I / O阻塞和synchronize
锁阻塞
public class InterruptExample {
private static class MyThread1 extends Thread {
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println("Thread run");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private static class MyThread1 extends Thread {
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println("Thread run");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new MyThread1();
thread1.start();
thread1.interrupt();
System.out.println("Main run");
}
Thread thread1 = new MyThread1();
thread1.start();
thread1.interrupt();
System.out.println("Main run");
}
Interrupted()
如果一个线程的run() 方法执行一个无限循环,并且没有执行sleep() 等会抛出InterruptedException 的操作
那么调用线程的Interrupt() 方法就无法使线程提前结束
那么调用线程的Interrupt() 方法就无法使线程提前结束
Executor 的中断操作
调用Executor 的shutdown() 方法会等待线程都执行完毕之后再关闭 , 但是如果调用的是 shutdownNow() 方法,
则相当于调用每个线程的Interrupt() 方法
则相当于调用每个线程的Interrupt() 方法
线程之间的协作
join
在线程中调用另一个线程的join() 方法,会将当前线程挂起,而不是忙等待
直到目标线程结束
在线程中调用另一个线程的join() 方法,会将当前线程挂起,而不是忙等待
直到目标线程结束
public class JoinExample {
private class A extends Thread {
@Override
public void run() {
System.out.println("A");
}
}
private class B extends Thread {
private A a;
B(A a) {
this.a = a;
}
@Override
public void run() {
try {
a.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B");
}
}
public void test() {
A a = new A();
B b = new B(a);
b.start();
a.start();
}
}
public static void main(String[] args) {
JoinExample example = new JoinExample();
example.test();
}
private class A extends Thread {
@Override
public void run() {
System.out.println("A");
}
}
private class B extends Thread {
private A a;
B(A a) {
this.a = a;
}
@Override
public void run() {
try {
a.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B");
}
}
public void test() {
A a = new A();
B b = new B(a);
b.start();
a.start();
}
}
public static void main(String[] args) {
JoinExample example = new JoinExample();
example.test();
}
wait() notify () notifyAll()
调用wait() 使得线程等待某个条件满足, 线程在等待时会被挂起,当其他线程运行是的这个条件满足时
其它线程会调用notify() 或者 notifyAll() 来唤醒挂起的线程
调用wait() 使得线程等待某个条件满足, 线程在等待时会被挂起,当其他线程运行是的这个条件满足时
其它线程会调用notify() 或者 notifyAll() 来唤醒挂起的线程
wait() 和 sleep() 的区别
wait()是Object的方法 , 而sleep() 是Thread 的静态方法
wait() 会释放锁 , sleep()不会
J.U.C - AQS
CountDownLatch
用来控制一个或者多个线程等待多个线程
CyclicBarrier
用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行
和CountDownLatch 相似 ,都是通过维护技术器来实现的,
线程执行await()方法之后计数器会减 1 ,并进行等待,直到
计数器为 0 ,所有调用await()方法而在等待的线程才能继续
执行
线程执行await()方法之后计数器会减 1 ,并进行等待,直到
计数器为 0 ,所有调用await()方法而在等待的线程才能继续
执行
CyclicBarrier 和 CountDownLatch 的一个区别是,
CyclicBarrier 的计数器通过调用reset() 方法可以循环
使用,所以它才叫做循序屏障
CyclicBarrier 的计数器通过调用reset() 方法可以循环
使用,所以它才叫做循序屏障
Semaphore
semaphore 类似于操作系统中的信号量,可以控制对互斥资源的访问线程数
FutureTask
FutureTask 实现了 RunnableFuture接口,该接口继承自Runnable 和 Future<V> ,
这使得FutureTask 即可以自当做一个任务执行,也可以有返回值
这使得FutureTask 即可以自当做一个任务执行,也可以有返回值
BlockingQueue
FIFO 队列 : LinedBlockingQueue . ArrayBlockingQueue(固定长度)
优先级队列 : PriorityBlockingQueue
提供了阻塞的take() 和 put() 方法,如果队列为空take() 将阻塞,直到队列中有内容
如果队列为满put() 将阻塞,直到队列有空闲位置
如果队列为满put() 将阻塞,直到队列有空闲位置
线程安全
0 条评论
下一页