一、并发集合
List(ArrayList|LinkedList)、Set(HashSet|TreeSet)、Map(HashMap|TreeMap)集合,这些集合只适合在单线程情况下使用。在Collecionts工具类中有synchronized开头方法可以把单线程集合转成支持并发的集合,但是效率不高,很少使用。
CopyOnWriteArrayList和CopyOnWriteArraySet都可以实现多线程异步访问。
ConcurrentHashMap是HashMap的多线程版本,在并发情况下使用
Map集合的主要特征是做数据的查询处理操作,所以在ConcurrentHashMap设计的时候考虑到了数据更新的安全性
与数据查询的并发性。
JDK1.7之前
ConcurrentHashMap采用锁分段机制,默认并发级别为16。 https://blog.csdn.net/weixin_40616523/article/details/86419754 这篇文章中分段锁讲的很形象
特点是写的时候同步写入,使用独占锁,读的时候为了保证性能使用了共享锁。
JDK1.8以后
ConcurrentHashMap写的时候采用CAS无锁算法进一步提高写入效率。
ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列,可以作为线程通信同步工具类使用。
二、同步工具类
CountDownLatch(闭锁):
让当前线程等待,等其它线程全部执行完,再执行当前线程。
下面案例。老板等员工人齐才会开会!
public static void main(String[] args) throws Exception {
ReentrantLock reentrantLock = new ReentrantLock();
CountDownLatch cdl = new CountDownLatch(10);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("-----------------------------人还未到齐,等会开会!");
try {
cdl.await();//让当前线程处于等待状态,除非锁存器计数为零。
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------------------------------人到齐了,开会!");
}
}).start();
for (int i = 0; i < 10; i++) {
int num=i+1;
new Thread(new Runnable() {
@Override
public void run() {
reentrantLock.lock();
System.out.println("第"+num+"名员工到达会议室!");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
cdl.countDown(); //减少锁存器计数,若值为零,释放所有等待线程
reentrantLock.unlock();
}
}).start();
}
}
CyclicBarrier(可循环的屏障)
其和CountDownLatch相似,这个是当一个线程到达屏障点,会进行等待,等到最后一个线程到达屏障点之后,这些拦
截的线程就会执行。
(1)构造函数 CyclicBarrier(int parties) 屏障拦截的线程数量
(2)await() 调用该方法时表示线程已经到达屏障,随即阻塞
public static void main(String[] args) {
ReentrantLock rl = new ReentrantLock();
CyclicBarrier cb = new CyclicBarrier(5);
for (int i=0;i<5;i++) {
new Thread(new Runnable() {
@Override
public void run() {
rl.lock();
System.out.println(Thread.currentThread().getName()+"来了");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
rl.unlock();
try {
cb.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"开始跑");
}
}).start();
}
}
//结果
Thread-0来了
//间隔一秒
Thread-1来了
//间隔一秒
Thread-2来了
//间隔一秒
Thread-4来了
//间隔一秒
Thread-3来了
//下面几乎同步执行
Thread-0开始跑
Thread-1开始跑
Thread-3开始跑
Thread-4开始跑
Thread-2开始跑
CountDownLatch和CyclicBarrier的区别
1. CountDownLatch只能用一次,CyclicBarrier可以reset(),且适合处理更复杂的业务
2. CyclicBarrier还有getNumberWaiting 获取当前阻塞的线程数量,isBroken()判断阻塞线程是否被中断。
Semaphore(信号量)
Semaphore 是 synchronized 的加强版,作用是控制线程的并发数量
比作控制车流的红绿灯,如马路要控制流量,只限制100辆车通行,其他必须在路口处等待,不能行驶在马路上,当其 中有5辆离开马路,那么允许后面5辆进入马路。
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire(); //获取一个锁
System.out.println(Thread.currentThread().getName() + ":doSomething start-" + new Date(System.currentTimeMillis()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + ":doSomething end-" + new Date(System.currentTimeMillis()));
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}