java 线程小结
1, 为什么wait与notify之前必须要加synchronized?
答案其实很简单,也是为了防止等待-通知机制出现race condition
为什么会出现race condition ?
答: 对象在被wait之前已经被另一线程notify , 之后的wait 会永久停止,并导致deadlock(死锁)
理想情况:
1, 第一个线程判断该对象是否要wait
2, 第一个线程将对象wait
3, 第二个线程再将对象notify
实际情况
1, 第一个线程判断该对象是否要wait
2, 第二个线程将对象notify
3, 第一个线程将对象wait
为了防止这些情况,才需要在wait与notify之前加synchronized
java 代码
非静态方法的执行:
); Thread.sleep(2000); log.info(sum.getAndIncrement()); sp.release(); } catch (InterruptedException e) { log.error("sleep error:", e); } }}?// 线程测试类public class RunThread { public static void main(String[] args) { final Logic logic = new Logic();? //定义一个producer Runnable test = new Runnable() { public void run() { logic.test(); } };? ExecutorService service = Executors.newCachedThreadPool();? for (int i = 0; i < 10; i++) { service.submit(test); }? service.shutdown(); }}注意; semaphore可以控制某个资源上读取操作的线程数量, 但是, semaphore本身是线程不安全的,
如果资源涉及到写入操作, 那么在操作中加上同步后, 信号量的作用也就跟Lock接口一样了.(一次只能执行一根线程)
8, 利用CyclicBarrier屏障接口实现,线程集合/解散功能
java有好多种的屏障实现, 简单的几种如下:
a, 利用条件变量Condition实现wait-notify机制,等待所有的线程都wait在某一个
集合点时,notifyAll一下. 缺点是需要一根监控线程
b, 利用join方法,开一个监视线程, 每次调用这个线程取被block住的线程数量.
当达到指定数量后, 监视线程自动死亡,以放开所有的被block threads.
c, 利用CyclicBarrier提供的功能,只需要在集合点处调用await()方法,即可.
); } catch (InterruptedException e) { log.error(e); } catch (BrokenBarrierException e) { log.error(e); } }? public void expression2() { try { Thread.sleep(2000); log.info(value*2); cyclic.await(); log.info(Thread.currentThread().getName() + " end."); } catch (InterruptedException e) { log.error(e); } catch (BrokenBarrierException e) { log.error(e); } }? public void expression3() { try { Thread.sleep(3000); log.info(value+2); cyclic.await(); log.info(Thread.currentThread().getName() + " end."); } catch (InterruptedException e) { log.error(e); } catch (BrokenBarrierException e) { log.error(e); } }}?// 线程测试类public class RunThread { public static void main(String[] args) { final Logic logic = new Logic(); Runnable run1 = new Runnable() { public void run() { logic.expression1(); } };? Runnable run2 = new Runnable() { public void run() { logic.expression2(); } };? Runnable run3 = new Runnable() { public void run() { logic.expression3(); } };? //各产生10个consumer和producer ExecutorService service = Executors.newCachedThreadPool();? service.submit(run1); service.submit(run2); service.submit(run3);? service.shutdown(); }} 注意: 使用屏障的时候, 小心异常的放生,当发生异常,所有线程都会被释放
等待中的线程将被中断. 且发生异常的屏障将不可用,需要屏障的实例reset一下.
9, 利用CountDownLatch接口实现线程集合/解散功能,类似CyclicBarrier,区别是倒数且只跑一次
接口方法与CyclicBarrier基本相同,不同在于构造函数需要传入一数量,表示
倒数的开始数量.以后会递减这个值