1、Java 实现同步的几种方式
方式一:使用 synchronized 关键字与 Object#wait()/notifyAll()
- Object#wait():释放当前锁,使该线程进入等待状态(阻塞状态)。
- Object#notify():在所有等待线程中随机唤醒一个线程,让它获得锁。
- Object#notifyAll():唤醒所有等待的线程,让它们一起竞争锁,最后其中之一获得锁。
方式二:使用 ReentrantLock(可重入锁)以及 Condition(条件)。
- Lock#lock():加锁。
- Lock#unlock():释放锁。
- Condition#await():释放当前锁,使该线程进入等待状态(阻塞状态)。
- Condition#signal():在所有等待线程中随机唤醒一个线程,让它获得锁。
- Condition#signalAll():唤醒所有等待的线程,让它们一起竞争锁,最后其中之一获得锁。
方式三:使用 Semaphore(信号量)
- new Semaphore(1):互斥的信号量。
- Semaphore#acquire():获取一个许可,如果没有就等待。
- Semaphore#release():释放一个许可。
注意:方式可能不仅仅上述几种,这里只是写出来相对应常见的三种方式。
- java.util.concurrent.* 并发包下的工具类,分别有:
- (1)ReentrantLock(可重入锁):指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。
- (2)Semaphore(信号量):是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。
- (3)CountDownLatch: 允许一个或多个线程等待其他线程完成操作。
- (4)CyclicBarrier :字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
2、Java 死锁
- 所谓死锁,就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
2.1 死锁触发的四大条件?
- (1)互斥使用:即当资源被一个线程使用(占有)时,别的线程不能使用;
- (2)不可抢占:即资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放;
- (3)请求与保持:即当资源请求者在请求其他的资源的同时保持对原有资源的占有;
- (4)循环等待:即存在一个等待队列:P1 占有 P2 的资源,P2 占有 P1 的资源,这样就形成了一个等待环路。
2.2 如何避免死锁?
- (1)加锁顺序:即线程按照一定的顺序加锁;
- (2)加锁时限:即线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁;
- (3)死锁检测。死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph 等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。
2.3 当检测出死锁后,这些线程该做些什么?
- 方案一:释放所有锁,回退,并且等待一段随机的时间后重试;
- 方案二:给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生死锁一样继续保持着它们需要的锁。如果赋予这些线程的优先级是固定不变的,同一批线程总是会拥有更高的优先级。为避免这个问题,可以在死锁发生的时候设置随机的优先级。
2.4 死锁示例:
public class TH_01_死锁 {
private final static Object lockA = new Object();
private final static Object lockB = new Object();
public static void main(String[] args) {
new ThreadA().start();
new ThreadB().start();
}
static class ThreadA extends Thread {
@Override
public void run() {
try {
System.out.println("ThreadA 开始执行");
synchronized (lockA) {
System.out.println("ThreadA 锁住了 lockA");
Thread.sleep(1000);
synchronized (lockB) {
System.out.println("ThreadA 锁住了 lockB");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
static class ThreadB extends Thread {
@Override
public void run() {
System.out.println("ThreadB 开始执行");
try {
synchronized (lockB) {
System.out.println("ThreadB 锁住了 lockB");
Thread.sleep(1000);
synchronized (lockA) {
System.out.println("ThreadB 锁住了 lockA");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
========== 运行结果 ==========
ThreadA 开始执行
ThreadA 锁住了 lockA
ThreadB 开始执行
ThreadB 锁住了 lockB
2.5 死锁解决方案示例
public class TH_01_死锁解决方案 {
private final static Object lockA = new Object();
private final static Object lockB = new Object();
public static void main(String[] args) {
new ThreadA().start();
new ThreadB().start();
}
static class ThreadA extends Thread {
@Override
public void run() {
try {
System.out.println("ThreadA 开始执行");
synchronized (lockA) {
System.out.println("ThreadA 锁住了 lockA");
Thread.sleep(1000);
synchronized (lockB) {
System.out.println("ThreadA 锁住了 lockB");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
static class ThreadB extends Thread {
@Override
public void run() {
System.out.println("ThreadB 开始执行");
try {
synchronized (lockA) {
System.out.println("ThreadB 锁住了 lockA");
Thread.sleep(1000);
synchronized (lockB) {
System.out.println("ThreadB 锁住了 lockB");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
========== 运行结果 ==========
ThreadA 开始执行
ThreadA 锁住了 lockA
ThreadB 开始执行
ThreadA 锁住了 lockB
ThreadB 锁住了 lockA
ThreadB 锁住了 lockB
3、AB 两条线程交替打印
public class TH_02_AB两条线程交替打印 {
private static int count = 0;
private final static Object object = new Object();
public static void main(String[] args) {
new ThreadA().start();
new ThreadB().start();
}
static class ThreadA extends Thread {
@Override
public void run() {
while (true) {
try {
synchronized (object) {
if (count < 100) {
if ((count & 1) == 1) {
object.wait();
}
System.out.println("ThreadA:" + count);
count++;
object.notifyAll();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class ThreadB extends Thread {
@Override
public void run() {
while (true) {
try {
synchronized (object) {
if (count < 100) {
if ((count & 1) == 0) {
object.wait();
}
System.out.println("ThreadB:" + count);
count++;
object.notifyAll();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
4、生产消费者
- 基于 synchronized 与 wait()/notifyAll()
public class TH_03_1_生产消费者模式synchronized {
private static int count = 0;
private final static int MAX_COUNT = 5;
private final static Object object = new Object();
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new ThreadA().start();
new ThreadB().start();
}
}
static class ThreadA extends Thread {
@Override
public void run() {
while (true) {
try {
synchronized (object) {
while (count == MAX_COUNT) {
System.out.println("数据已满");
object.wait();
}
count++;
System.out.println("生产者产生了一个数据,当前总数:" + count);
object.notifyAll();
}
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class ThreadB extends Thread {
@Override
public void run() {
while (true) {
try {
synchronized (object) {
while (count <= 0) {
System.out.println("数据为空");
object.wait();
}
count--;
System.out.println("消费者消费了一个数据,当前总数:" + count);
object.notifyAll();
}
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
- 基于 ReentractLock(可重入锁) 和 Condition(条件)
public class TH_03_2_生产消费者模式ReentractLock {
private static int count = 0;
private final static int MAX_COUNT = 5;
private final static Lock lock = new ReentrantLock();
private final static Condition fullCondition = lock.newCondition();
private final static Condition emptyCondition = lock.newCondition();
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new ThreadA().start();
new ThreadB().start();
}
}
static class ThreadA extends Thread {
@Override
public void run() {
while (true) {
try {
lock.lock();
while (count == MAX_COUNT) {
System.out.println("数据已满");
fullCondition.await();
}
count++;
System.out.println("生产者产生了一个数据,当前总数:" + count);
emptyCondition.signalAll();
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
static class ThreadB extends Thread {
@Override
public void run() {
while (true) {
try {
lock.lock();
while (count < 1) {
System.out.println("数据为空");
emptyCondition.await();
}
count--;
System.out.println("消费者消费了一个数据,当前总数:" + count);
fullCondition.signalAll();
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
}
public class TH_03_3_生产消费者模式Semaphore {
private static int count = 0;
private static final int MAX_COUNT = 5;
private static final Semaphore semaphore = new Semaphore(1);
private static final Semaphore fullSemaphore = new Semaphore(MAX_COUNT);
private static final Semaphore emptySemaphore = new Semaphore(0);
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new ThreadA().start();
new ThreadB().start();
}
}
static class ThreadA extends Thread {
@Override
public void run() {
while (true) {
try {
fullSemaphore.acquire();
semaphore.acquire();
count++;
System.out.println("生产者产生了一个数据,当前总数:" + count);
semaphore.release();
emptySemaphore.release();
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class ThreadB extends Thread {
@Override
public void run() {
while (true) {
try {
emptySemaphore.acquire();
semaphore.acquire();
count--;
System.out.println("消费者消费了一个数据,当前总数:" + count);
semaphore.release();
fullSemaphore.release();
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
5、让多线程按照自己指定的顺序执行
public class TH_04_1_多线程按指定顺序执行join {
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程1执行了...");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
thread1.join();
System.out.println("线程2执行了...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
try {
thread2.join();
System.out.println("线程3执行了...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread2.start();
thread3.start();
thread1.start();
}
}
- 基于 synchronized 与 wait()/notifyAll()
public class TH_04_2_多线程按指定顺序执行synchronized {
private static Object object1 = new Object();
private static Object object2 = new Object();
private static Boolean threadRun1 = false;
private static Boolean threadRun2 = false;
public static void main(String[] args) {
final Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (object1) {
System.out.println("线程1执行了...");
threadRun1 = true;
object1.notify();
}
}
});
final Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (object1) {
try {
if (!threadRun1) object1.wait();
synchronized (object2) {
System.out.println("线程2执行了...");
object2.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (object2) {
try {
if (!threadRun2) object2.wait();
System.out.println("线程3执行了...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread3.start();
thread2.start();
thread1.start();
}
}
- 基于 ReentractLock(可重入锁) 和 Condition(条件)
public class TH_04_3_多线程按指定顺序执行ReentrantLock {
private static Lock lock = new ReentrantLock();
private static Condition condition1 = lock.newCondition();
private static Condition condition2 = lock.newCondition();
private static Boolean threadRun1 = false;
private static Boolean threadRun2 = false;
public static void main(String[] args) throws InterruptedException {
final Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
System.out.println("线程1执行了...");
threadRun1 = true;
condition1.signalAll();
lock.unlock();
}
});
final Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try {
if (!threadRun1) condition1.await();
System.out.println("线程2执行了...");
threadRun2 = true;
condition2.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
}
});
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try {
if (!threadRun2) condition2.await();
System.out.println("线程3执行了...");
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread3.start();
thread2.start();
thread1.start();
}
}
public class TH_04_4_多线程按指定顺序执行Semaphore {
private static Semaphore semaphore1 = new Semaphore(1,true);
private static Semaphore semaphore2 = new Semaphore(0);
private static Semaphore semaphore3 = new Semaphore(0);
public static void main(String[] args) {
final Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore1.acquire();
System.out.println("线程1执行了...");
semaphore2.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
final Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore2.acquire();
System.out.println("线程2执行了...");
semaphore3.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
final Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore3.acquire();
System.out.println("线程3执行了...");
semaphore1.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread3.start();
thread2.start();
thread1.start();
}
}
6、参考文献
- https://developer.aliyun.com/article/52846#slide-4
- https://www.liaoxuefeng.com/wiki/1252599548343744/1306580960149538
- https://fangjian0423.github.io/2016/04/18/java-synchronize-way/
- https://zhuanlan.zhihu.com/p/80787379