闭锁是一种同步工具类,可以延迟线程的进度直到其到达终点状态。
(一)常用的场景如下:
1)确保某个计算在其需要的所有资源都被初始化之后才继续执行。二元闭锁(包括两个状态)可以用来表示"资源R已经被初始化",而所有需要R的操作都必须先在这个闭锁上等待。
2)等待直到某个操作的所有参与者都就绪时(例如,在多玩家游戏中所有的玩家)再继续执行。在这种情况中,当所有的玩家都准备就绪时,闭锁将到达结束的状态。
(二)闭锁在java中常见的实现方式
1)CountDownLatch实现闭锁.
问题:使用闭锁来测试n个线程并发执行某个任务时所需要的时间.
思路:使用两个闭锁,分别为’起始门’ 和’结束门’。起始门计数器的初始值为1,而结束门的计数器初始值为工作线程的数量。每个工作线程首先要做的事就是在启动门上等待,从而确保所有线程都就绪后才能执行。而每个线程要做的最后一件事就是将调用结束门的countDown方法减1,这能 使主线程高效的等待直到所有工作线程都执行完成,从而统计所消耗的时间。
* @author zpf
* @date 2018年12月28日
* 闭锁是一个同步工具类,可以延迟线程的进度直到其到达终点状态。
* 使用闭锁来测试n个线程并发执行某个任务时所需要的时间.
*/
public class Demo1 {
public long timeTasks(int nThreads,final Runnable task) {
//启动门--保证主线程可以同时释放所有的工作线程
final CountDownLatch startGate = new CountDownLatch(1);
//结束门--保证主线程可以等待最后一个线程执行完成
final CountDownLatch endGate = new CountDownLatch(nThreads);
for(int i = 0;i < nThreads;i++) {
Thread t = new Thread() {
@Override
public void run() {
try {
startGate.await();
try {
task.run();
} catch (Exception e) {
// TODO: handle exception
}finally {
endGate.countDown();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
long start = System.nanoTime();
//同时释放所有的工作线程
startGate.countDown();
try {
//有效的保证了所有线程的执行完成
endGate.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long end = System.nanoTime();
return end - start;
}
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
//模拟线程在执行任务
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
long time = Demo1.timeTasks(10, runnable);
System.out.println(time);
}
}
2.使用FutureTask来实现闭锁
应用场景: 使用FutureTask来执行一个高开销的计算,并且计算结果将在稍后使用. 通过提前启动计算,可以减少在等待结果时需要的时间.
特点:FutureTask将计算结果从执行计算的线程传递到获取这个结果的线程,并确保这种传递过程能实现结果的安全发布
/**
* @author zpf
* @date 2019年1月2日
*
* Future.get的行为取决于任务的状态。如果任务已经完成,那么get会立即返回结果,否则get将阻塞知道任务进入完成状态, 然后返回结果或者抛出异常.
*
*/
public class Preloader {
private final FutureTask<ProduceInfo> future = new FutureTask<ProduceInfo>(new Callable<ProduceInfo>() {
@Override
public ProduceInfo call() throws Exception {
// TODO Auto-generated method stub
return loadProductInfo();
}
});
private ProduceInfo loadProductInfo() {
try {
Thread.sleep(6666);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return new ProduceInfo("aaa");
}
private final Thread thread = new Thread(future);
public void start() {
thread.start();
}
public ProduceInfo get() throws Throwable {
try {
return future.get();
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof InterruptedException) {
throw cause;
}
}
return null;
}
public static void main(String[] args) throws Throwable {
Preloader preloader = new Preloader();
//提前启动以减少等待时间
preloader.thread.start();
//Thread.sleep(millis);
ProduceInfo produceInfo = preloader.get();
System.out.println(produceInfo.getName()+"");
}
}
class ProduceInfo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ProduceInfo(String name) {
this.name = name;
}
}