Synchoronizer 接下来说一下同步辅助类的相关内容 1、闭锁(Latch) 闭锁的作用是:延迟线程的进度直到线程达到终点。 原理:闭锁相当于一个大门,在大门打开之前,也就是终点状态到来之前,没有一个线程能够通过,都处于阻塞状态。一旦终点状态到来,大门打开,则允许所有线程通过。一旦闭锁达到终点状态,它就不能改变状态了。 闭锁可以用来确保特定活动直到其他活动完成之后才发生。
CountDownLatch 一个闭锁的实现类,初始化为一个正数。闭锁的状态是一个计数器,当计数器=0时,则达到终点状态。 两个主要方法, countDown():计数器-1 await():当计算器!=0时,则该线程一直阻塞到计算器=0。或者等待到线程终点或者超时。 看代码:
package sychrionizer; import java.util.concurrent.CountDownLatch; public class LathchTest { public long timeTasks(int sThreads , final Runnable task) throws InterruptedException{ //开始阀门,初始化计数器为1 final CountDownLatch startGate = new CountDownLatch(1); //结束阀门 final CountDownLatch stopGate = new CountDownLatch(sThreads); for(int i = 0 ; i < sThreads ; i++){ Thread d = new Thread(){ public void run() { try { System.out.println("线程"+ Thread.currentThread().getName() + "等待开始标志..."); startGate.await(); try { task.run(); } finally { System.out.println("线程"+ Thread.currentThread().getName() + "结束标志-1"); stopGate.countDown(); } } catch (Exception }; d.start(); } //计时 long start = System.nanoTime(); System.out.println("开始标志位-1,开发阀门打开....."); startGate.countDown(); System.out.println("结束标志阻塞,等待结束阀门..."); stopGate.await(); long stop = System.nanoTime(); return stop - start; } public static void main(String[] args) { final Runnable task = new Runnable() { public void run() { System.out.println("任务"+Thread.currentThread().getName() + "正在执行"); } }; try { System.out.println(new LathchTest().timeTasks(10, task)); } catch (InterruptedException e) { e.printStackTrace(); } } }FutureTask 也可以作为闭锁,FutureTask是通过Callable实现的,它等价于一个可携带结果的Runnable,FutureTask具有三个状态,等待、运行、完成。完成状态包括以任务方式结束,比如正常结束、取消、异常。一旦FutureTask进入完成状态,FutureTask会永远停止在这个状态上面。 主要方法: get(),依赖于任务的状态,如果任务完成,则get可以直接获取到结果。否则,一直等待,知道获取到结果或者抛出异常。 来看看小例子
package sychrionizer; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class FutureTaskTest { private final FutureTask<String> future = new FutureTask<String>(new Callable<String>() { public String call() throws Exception { System.out.println("结果执行中...."); return "结果..是[1231231231]"; }; }); private final Thread thread = new Thread(future); /* * 对外启动线程的方法 */ public void start(){ System.out.println("开始计算"); thread.start(); } /* * 对外获取结果的线程 */ public String get(){ try { System.out.println(Thread.currentThread().getName() + "线程准备获取结果!"); return future.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } return null; } public static void main(String[] args) { final FutureTaskTest future = new FutureTaskTest(); // 开关打开 //开始计算 future.start(); for(int i = 0 ; i < 10 ; i++){ Thread t = new Thread(){ public void run() { System.out.println(future.get()); } }; t.start(); } // 开关打开 //开始计算 //future.start(); } }测试代码有两种情况,一种是,future.start();写在上面,运行结果是:
开始计算 结果执行中.... Thread-1线程准备获取结果! 结果..是[1231231231] Thread-2线程准备获取结果! 结果..是[1231231231] Thread-3线程准备获取结果! 结果..是[1231231231] Thread-4线程准备获取结果! 结果..是[1231231231] Thread-5线程准备获取结果! 结果..是[1231231231] Thread-6线程准备获取结果! 结果..是[1231231231] Thread-7线程准备获取结果! 结果..是[1231231231] Thread-8线程准备获取结果! 结果..是[1231231231] Thread-9线程准备获取结果! 结果..是[1231231231] Thread-10线程准备获取结果! 结果..是[1231231231]这种情况就是先计算出结果,再去get,可以直接get到结果 第二种情况是start在下面,也就是说先get,再计算,输出是这样的:
Thread-2线程准备获取结果! Thread-6线程准备获取结果! Thread-7线程准备获取结果! Thread-5线程准备获取结果! Thread-4线程准备获取结果! Thread-3线程准备获取结果! Thread-1线程准备获取结果! 开始计算 Thread-10线程准备获取结果! Thread-8线程准备获取结果! Thread-9线程准备获取结果! 结果执行中.... 结果..是[1231231231] 结果..是[1231231231] 结果..是[1231231231] 结果..是[1231231231] 结果..是[1231231231] 结果..是[1231231231] 结果..是[1231231231] 结果..是[1231231231] 结果..是[1231231231] 结果..是[1231231231]上面的结果说吗,get()方法如果获取不到结果,则一直等待到结果为止。
2、Semaphore(信号量)
Semaphore用来控制能够同时访问某特定资源的活动数量。 Semaphore原理:一个Semaphore管理一个有效的许可集(premit),许可的初始量通过构造方法传入Semaphore。活动能够获得许可(在有剩余许可的前提下),并且在使用之后释放改许可。如果没有许可了,那么acquire会被阻塞,直到有用的许可为止或者中断或者操作超时。 主要方法:acquire()请求许可,有空余,则占领,否则被阻塞,等待一直有空闲的许可位置 release()释放许可
举个简单的例子,上厕所,共有两个坑,是个人排队,一次性只能上两个人,其余8人的请求只能等待,一直到有坑位位置。
package sychrionizer; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; public class MySemaphore implements Runnable{ //测试位置 private Semaphore position; private int id; public MySemaphore(Semaphore position , int id){ this.id = id; this.position = position; } public void run() { try { if(position.availablePermits() > 0){ System.out.println("线程"+ this.id + "进入,发现有空位"); }else{ System.out.println("线程"+ this.id + "进入,发现没有空位"); } //获取到空位置 position.acquire(); System.out.println("线程"+ this.id + "获得了该位置"); Thread.sleep(1000); System.out.println("线程"+ this.id + "使用完毕,离开了该位置"); position.release(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { ExecutorService pool = Executors.newCachedThreadPool(); Semaphore position = new Semaphore(2);//一共有两个坑位 //10 for(int i = 0 ; i < 10 ; i++){ pool.submit(new MySemaphore(position, i)); } //释放线程池 pool.shutdown(); position.acquireUninterruptibly(2); System.out.println("使用完毕,清扫"); position.release(2); } }以上例子就说明了信号量的使用过程。