线程互斥
//线程等待
synchronized(锁对象){
//要保护的代码,作为原子的整体执行,(只能有一个线程执行,当这个线程执行时,其他线程都在synchronized处等待其他线程执行完毕
}
//等待锁内线程执行完毕后会释放锁,其他等待的线程再去争抢锁对象,是有线程能够进入锁对象
// 加锁时一定要注意加的锁是否为同一把锁
synchronized关键字的两种方法
-
加在普通方法上
public synchronized void method(){ //这时 synchronized锁是this对象 } //等价于 class Test{ synchronized(this){ //方法内容 } }
-
加在静态方法上
public synchronized static void method(){ //这时 synchronized锁的是 Test.class对象(代表当前类,而且在内存中唯一) } //等价于 public static void method(){ synchronized (Test.class){ //方法内容 } }
ReentrantLock锁
//创建ReentrantLock锁对象
ReentrantLock lock = new ReentrantLock();
lock.lock();//加锁
//需要保护的代码(只能有一个线程)
lock.unlock();//解锁
//unlock建议放在finally中确保它一定会被执行
//可以使用ReentrantLock(true)创建公平锁 指在锁外等待时的排队顺序
new ReentrantLock(true)//创建公平锁
在执行效率上非公平锁的效率要高于非公平的效率
死锁
- 如果一个线程尝试获得多把锁,可能会产生死锁
- 每个线程获得锁的顺序相反就会死锁
- 线程1 获取 锁A, 获取 锁B
- 线程2 获取 锁B, 获取 锁A
- 如果按相同的顺序获得锁时,不会有死锁现象
线程同步
-
synchronized: (悲观锁)
-
wait: 等待
-
notify, notifyAll
- notify: 是唤醒锁对象上随机的一个线程
- notifyAll: 是唤醒锁对象上所有等待的线程都唤醒
-
阻塞队列
while(true){ synchronized(锁对象){ //条件不满足时唤醒其他线程 这条线程进入等待状态 锁对象.notifyAll();//唤醒其他线程 锁对象.wait();//这条线程等待 //当这条线程被唤醒时则代码继续往下执行 } }
BlockingQueue - Blocking(阻塞 - 只条件不满足时,线程等待) Queue队列
- put: 加入元素,当队列满了就会阻塞
- take: 取出元素,当队列空的时候就会阻塞
线程的六种状态
-
new : 创建了线程对象
-
runnable : 线程可运行状态
-
waiting : 进入等待
-
timed_waiting : 进入等待(有时限)
-
blocked : 阻塞
-
terminated : 终结
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SIwNPnH9-1596005368380)(E:\JavaSEReinforce\summary\img\线程的六种状态.png)]
五种状态:
- 新建
- 就绪
- 运行(java可运行)
- 终止
- 阻塞(java阻塞 + 等待 + 有实效的等待
补充
wait和sleep的区别
Object中的方法,所有对象都能继承,sleep是Thread类中静态方法
wait必须配合synchronized一起使用,sleep没有此限制
wait方法调用后会释放锁对象供其他线程使用,而sleep并不会释放锁
Synchronized 与ReentrantLock的区别
synchronized自动释放锁
ReetrantLock调用unlock(),并且建议放在finally中(防止上面代码出现异常从而导致无法释放锁对象)
synchronized关键字,语法直接支持,ReentrantLock是Java中的一个类,需要创建对象才能使用
ReentrantLock支持公平锁(在锁外面排队的线程可以按照先后顺序进入锁)使用new ReentrantLock(true)创建公平锁
当出现多把锁的嵌套时按照相同的顺序获取锁,可以有效避免出现死锁
Java中的一个类,需要创建对象才能使用
ReentrantLock支持公平锁(在锁外面排队的线程可以按照先后顺序进入锁)使用new ReentrantLock(true)创建公平锁
当出现多把锁的嵌套时按照相同的顺序获取锁,可以有效避免出现死锁