1、CountDownLatch:用来同步一个或多个任务,强制他们等待由其他任务执行的一组操作完成。CountDownLatch初始化可以设置一个数值,任何在这个对象上调用await都将阻塞,直至计数值为0.其他任务结束其工作的时候,调用countdown()来减小这个计数值。注意:CountDownLatch的计数值一旦设置就不能再更改,如果需要能够更改计数值的版本,则可以使用CyclicBarrier。示例:
Runnable A:
private finnal CountDownLatch latch;
public void run(){
try{
doWork();
latch.countDown();
}catch(Exception e){
//
}
}
Runnable B:
private finnal CountDownLatch latch;
public void run(){
try{
latch.await();
。。。
}catch(Exception e){
//
}
}
public class CountDownLatchDemo{
static final int SIZE =10;
public static void main(String[] args){
ExecutorService exe =Executors.newCachedThreadPool();
CountDownLatch latch = new latch(SIZE);
for(int i=0;i<10;i++){
exe.execute (new RunnableAClass(latch ));
}
exe.execute (new RunnableBClass(latch ));
exe .shutDown();
}
}
2、Random.nextInt()是线程安全的。3、CyclicBarrier非常像CountDownLatch,只不过CountDownLatch是只能触发一次的,而CyclicBarrier可以多次重用。
4、DelayQueue:这是一个用于放置实现Delay接口对象的无界BlockingQueue,其中对象只能在其到期之后才能从队列中取走,这种队列是有序的,如果没有任何延迟到期,那么就不会有任何头元素,并且Poll会返回null(正因为这样。你不能将null放置到这种队列中);
该队列是如何实现排序的呢?Delay接口还继承了Compareable接口,用于产生合理的比较。Delay接口还有一个getDelay方法,用于告知延迟到期的时间。
5、PriorityBlockingQueue:这是一个很基础的优先级队列,它具有可阻塞的读取操作,队列之中按照优先级的顺序进行排列。这种队列的阻塞特性提供了所有必须的同步,不需要你进行显示的同步。
6、Semaphore:计数信号量允许n个任务同时访问这个资源。你可以将信号量当做是访问资源的许可证。比如对象池(自定义的)就可以使用Semaphore,对象池允许N个任务同时访问,但是对象池中对象的获取就需要获得许可证(Semaphore.available)才可以,使用完之后,在返还许可证(Semaphore.release)即可.
7、ExChanger;是在两个任务之间交换对象的栅栏。典型的应用场景是:一个任务在创建对象,而对象的创建的代价高昂,而另一个任务在消费这些对象,通过这种方式可以有更多的对象在被创建的同时被消费掉。简单地说就是两个线程,一个是生产者一个是消费者,生产者生产的东西可以同时用于消费者消费,这有一个前提是这两个线程使用同一个ExChanger。生产者生产一定的产品之后,可以通过ExChanger.exchange方法将产品(通常是一个List)给另一个线程。注意:生产者调用exchange方法之后,它将阻塞,直至消费者线程也用exchange。
8、Synchronized与Atomic、Lock的使用原则:只有在需要性能调优的时候才会使用Lock或者Atomic,否则使用Synchronized.
9、免锁容器:CopyOnWriteArray***,Concurrent****。首先,像传统的Vector、HashTable这类早期的容器都是线程安全的,当用于非多线程的应用程序的时候,就会产生不可接受的开销。后来,Java出现了新的不同步的容器类,并且Collections提供了各种static方法来同步不同类型的容器,但是这种开销仍旧是基于Synchronized加锁机制的。SE5开始,Java提供了新的容器类,通过更加灵巧的技术消除加锁,从而提高线程安全的性能。
免锁容器背后的通用策略:对容器的修改和读取可以同时发生,只要读取者只能看到完成修改的结果即可。修改是在容器数据结构的某个部分的一个单独的副本(有时是整个数据结构的副本)上执行的,并且这个副本在修改的过程中是不可视的,只有修改完成之后,被修改的数据结构才会主动的与主数据结构进行交换,之后读取者就可以看到这个修改了。
在性能方面,免锁容器是优于Synchronize的。
Concurrent****允许并发的读取和写入,采用了类似上述策略的技术,但是添加写入者对性能造成的影响没有CopyOnWriteArray***明显。
10、“乐观加锁”,所谓的“乐观”其实是这里没有加锁没有互斥。我们保持数据是未加锁状态,并希望没有任何其他任务可以修改它。Atomic类为我们进行“乐观加锁”提供了可能。在某项计算完成的时候,你需要使用一个称为compareAndSet()的方法,你将新旧值一起提交给这个方法,如果旧值与它在Atomic对象中发现的值不一致,那么操作就失败了。
11、ReadWriteLock:对向数据结构相对不频繁的写入,但是有多个任务要经常读取数据结构的这类情况进行了优化。ReadWriteLock可以使得你可以同时有多个读取者,只要它们不试图写入即可。如果写锁已经被其他任务持有,那么任何读取者都不能访问,直至写锁被释放。注意:写入者的数量要远小于读取者的数量。