1.什么是同步容器类?
同步容器有 vector,hashtable等,他们的实现方式是:封装类的状态,对每个公有方法都使用同步,使得每次只有一个线程能访问容器的状态。
对于同步容器每个方法来说,它是线程安全的
但是在客户端代码中,一些复合操作仍然会出现并发性问题。
常见的复合操作:
迭代
跳转
条件运算 若没有则添加
解决方法:
在客户端对容器加锁。
带来的另外一个问题:
性能,长时间的对容器加锁会降低程序的可伸缩性,持有锁的时间越长,锁上的竞争就可能更加激烈。
2.什么是并发容器?
并发容器是用来替代同步容器,可以极大的提高伸缩性并降低风险。
常见的
ConcurrentHashMap
使用的颗粒度更细的加锁机制,分段锁
size和is empty的值变成了一个估算值
但是get put containkey 和remove的性能更加优化
CopyOnWriteArrayList
只要正确发布一个事实不可变的对象,在访问该对象时就不再需要进一步的同步
3.同步工具类
信号量, 栅栏, 闭锁
闭锁
作用:可以延迟线程的进度直到其达到中止状态。
CountDownLatch
所有的线程都必须等待CountDownLatch的计数变成0的时候才会同时执行.
FutureTask
异步任务,调用后继续执行下一个代码,待需要用到结果时通过get方法来获取结果
Semaphore
信号量,用来控制同时访问某个特定资源的操作数量,
在实际的应用层中也可以用信号量来实现限流
如:
public class SemaphoreTest {
private static ExecutorService executorService = Executors.newCachedThreadPool();
public static void main(String[] args) {
final Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 20; i++) {
Runnable runnable = () -> {
try {
semaphore.acquire();
Thread.sleep((long) Math.random());
semaphore.release();
System.out.println("剩余许可:"+semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
};
executorService.submit(runnable);
}
executorService.shutdown();
}
}
栅栏
类似于闭锁,区别在于,所有线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,栅栏用于等待其他线程。
exchanger 两方栅栏,各方在栅栏位置上交换数据。