用死循环的方式,一直尝试获取锁。在任务耗时比较长或者并发冲突比较大的时候,就不适合。
因为任务耗时比较长或者并发冲突比较大的时候,可能要循环上万次都不止,才能获取到锁,太消耗 CPU 。
这种场景下,理想的解决方案:
在 Java 中实现线程等待和唤醒,有多种方式,比较常见的有以下两种:
1、synchronized 加 wait()、notify()、notifyAll()
看下这个例子
package constxiong.concurrency.a025;
import java.util.HashSet;
import java.util.Set;
/**
* 测试 线程等待/唤醒
* @author ConstXiong
* @date 2019-09-27 15:44:48
*/
public class TestWaitAndNotify {
//单例的资源管理类
private final static MangerWaitAndNotify manager = new MangerWaitAndNotify();
//资源1
private static Object res1 = new Object();
//资源2
private static Object res2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
manager.applyResources(res1, res2);
//向管理类,申请res1和res2,申请失败,重试
try {
System.out.println("线程:" + Thread.currentThread().getName() + " 申请 res1、res2 资源成功");
synchronized (res1) {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取到 res1 资源的锁");
//休眠 3 秒
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (res2) {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取到 res2 资源的锁");
}
}
} finally {
manager.returnResources(res1, res2);//归还资源
}
}).start();
new Thread(() -> {
manager.applyResources(res1, res2);
try {
System.out.println("线程:" + Thread.currentThread().getName() + " 申请 res1、res2 资源成功");
synchronized (res2) {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取到 res2 资源的锁");
//休眠 1秒
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (res1) {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取到 res1 资源的锁");
}
}
} finally {
manager.returnResources(res1, res2);//归还资源
}
}).start();
}
}
/**
* 资源申请、归还管理类
* @author ConstXiong
* @date 2019-09-24 14:10:57
*/
class MangerWaitAndNotify {
//资源存放集合
private Set<Object> resources = new HashSet<Object>();
/**
* 申请资源
* @param res1
* @param res2
* @return
*/
synchronized void applyResources(Object res1, Object res2) {
while (resources.contains(res1) || resources.contains(res1)) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
resources.add(res1);
resources.add(res2);
}
/**
* 归还资源
* @param res1
* @param res2
*/
synchronized void returnResources(Object res1, Object res2) {
resources.remove(res1);
resources.remove(res2);
notifyAll();
}
}
打印结果
线程:Thread-0 申请 res1、res2 资源成功
线程:Thread-0 获取到 res1 资源的锁
线程:Thread-0 获取到 res2 资源的锁
线程:Thread-1 申请 res1、res2 资源成功
线程:Thread-1 获取到 res2 资源的锁
线程:Thread-1 获取到 res1 资源的锁
2、Condition、await()、signal()、signalAll()
示例代码
package constxiong.concurrency.a025;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 测试 线程等待/唤醒
* @author ConstXiong
* @date 2019-09-27 15:44:48
*/
public class TestAwaitAndSignal {
//单例的资源管理类
private final static MangerAwaitAndNotify manager = new MangerAwaitAndNotify();
//资源1
private static Object res1 = new Object();
//资源2
private static Object res2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
manager.applyResources(res1, res2);
//向管理类,申请res1和res2,申请失败,重试
try {
System.out.println("线程:" + Thread.currentThread().getName() + " 申请 res1、res2 资源成功");
synchronized (res1) {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取到 res1 资源的锁");
//休眠 3 秒
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (res2) {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取到 res2 资源的锁");
}
}
} finally {
manager.returnResources(res1, res2);//归还资源
}
}).start();
new Thread(() -> {
manager.applyResources(res1, res2);
try {
System.out.println("线程:" + Thread.currentThread().getName() + " 申请 res1、res2 资源成功");
synchronized (res2) {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取到 res2 资源的锁");
//休眠 1秒
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (res1) {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取到 res1 资源的锁");
}
}
} finally {
manager.returnResources(res1, res2);//归还资源
}
}).start();
}
}
/**
* 资源申请、归还管理类
* @author ConstXiong
* @date 2019-09-24 14:10:57
*/
class MangerAwaitAndNotify {
//资源存放集合
private Set<Object> resources = new HashSet<Object>();
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
/**
* 申请资源
* @param res1
* @param res2
* @return
*/
void applyResources(Object res1, Object res2) {
while (resources.contains(res1) || resources.contains(res1)) {
lock.lock();
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
resources.add(res1);
resources.add(res2);
}
/**
* 归还资源
* @param res1
* @param res2
*/
void returnResources(Object res1, Object res2) {
lock.lock();
try {
resources.remove(res1);
resources.remove(res2);
condition.signalAll();
} finally {
lock.unlock();
}
}
}
打印结果
线程:Thread-0 申请 res1、res2 资源成功
线程:Thread-0 获取到 res1 资源的锁
线程:Thread-0 获取到 res2 资源的锁
线程:Thread-1 申请 res1、res2 资源成功
线程:Thread-1 获取到 res2 资源的锁
线程:Thread-1 获取到 res1 资源的锁
ConstXiong 备案号:苏ICP备16009629号-3