文章目录
集合类
CopyOnWriteArrayList
- 对比传统list
// List<String> list = new ArrayList<>();
//并发下ArrayList不安全,需要Synchronized
/**
* 解决方法
* 1. new Vector<>();
* 2. Collections.synchronizedCollection(new ArrayList<>())
* 3. new CopyOnWriteArrayList<>();
*/
//CopyOnWrite 写入时复制 COW思想:计算机程序设计领域的一种优化策略
//多个线程调用的时候,list,读取的时候,固定的,写入(可能覆盖)
//在写入的时候避免覆盖(读写分离思想 )
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
System.out.println(list);
- add方法不同
//CopyOnWriteArrayList
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
// Vector
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
CopyOnWriteSet
//ConcurrentModificationException:并发修改异常
public static void main(String[] args) {
// HashSet<String> set = new HashSet<>();
/**
* 解决方法
* 1. Collections.synchronizedCollection(new HashSet<String>());
* 2. new CopyOnWriteArraySet<>();
*/
CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
},String.valueOf(i)).start();
}
}
源码
- 底层存储结构是CopyOnWriteArrayList
private final CopyOnWriteArrayList<E> al;
- add方法:调用copyOnWriteArrayList的addIfAbsent方法
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();
// indexOf遍历整个数组,返回e在snapshot的下标,没有则返回-1
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
// addIfAbsent
addIfAbsent(e, snapshot);
}
- add方法:对比最新的数组
private boolean addIfAbsent(E e, Object[] snapshot) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] current = getArray();
int len = current.length;
// 如果最新数组跟当前数组不同
if (snapshot != current) {
// Optimize for lost race to another addXXX operation
int common = Math.min(snapshot.length, len);
// 对比每个数据,如果最新的第i个数据不同于原数组的第i个且等于e,则失败
for (int i = 0; i < common; i++)
if (current[i] != snapshot[i] && eq(e, current[i]))
return false;
// 如果最新的数组里有e,失败
if (indexOf(e, current, common, len) >= 0)
return false;
}
// 新建一个数组,长度比之前+1,最后一个为新添加的元素
Object[] newElements = Arrays.copyOf(current, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
ConcurrentHashMap
//map 是这样用的吗? 不是,工作中不这么用
// new HashMap<>()
//默认等价于什么 new HashMap<>(16,0.75)
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 0; i < 20; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
Callable
基本
- 可以有返回值
- 可以抛出异常
- 方法不同,run()/call()
代码
- Callable
即传入什么返回什么
- 使用关系
try {
//new Thread(new Runnable() {}).start();
//new Thread(new FutureTask<V>()).start();
//new Thread(new FutureTask<V>(Callable)).start();
MyThread myThread = new MyThread();
FutureTask futureTask = new FutureTask(myThread);//适配类
new Thread(futureTask,"A").start(); //可以执行
new Thread(futureTask,"B").start(); //结果会被缓存,效率高
//获得返回值,可能会产生阻塞
//把他放到最后,或者异步通信来处理
Integer o = (Integer) futureTask.get();
} catch (Exception e) {
e.printStackTrace();
}
- 有缓存
- 结果可能需要等待,会阻塞
常用的辅助类
1. CountDownLatch
允许一个或多个线程
等待直到 在其他线程中执行的一组操作完成的
同步辅助。
// 计数器,总数是6,必须要执行任务的时候再使用
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"GoOut");
//数量-1
countDownLatch.countDown();
},String.valueOf(i)).start();
}
//等待计数器归零,再向下执行
countDownLatch.await();
System.out.println("Close door!");
countDownLatch.countDown(); // 数量-1
countDownLatch.await(); //等待计数器归零,再向下执行
await 方法
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// Sync类内方法
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0) // 重点方法
doAcquireSharedInterruptibly(arg);
}
// 该方法在AbstractQueuedSynchronizer中,
// 其中tryAcquireShared由CountDownLatch重写
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg); // 重点
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
2. CyclicBarrier
允许一组线程
全部等待彼此达到共同屏障点
的同步辅助。
// 集齐7颗龙珠才能召唤神龙
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
//当await的线程数量到达7之后,执行下面内容,再执行其他await线程
System.out.println("召唤神龙");
});
for (int i = 0; i < 6; i++) {
//lambda只能拿到final变量
final int temp = i; // jdk1.8默认加上final
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"收集"+ temp +"龙珠");
try{
//等待直到加法器加到7之后,,先执行加法器内容,其他线程再一起执行后面内容
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName()+"销毁"+ temp +"龙珠");
}catch(Exception e){
e.printStackTrace();
}
},String.valueOf(i)).start();
}
3. Semaphore
一个计数信号量。 在概念上,信号量维持一组许可证。 如果有必要,每个acquire()都会阻塞,直到许可证可用,然后才能使用它。 每个release()添加许可证,潜在地释放阻塞获取方。 但是,没有使用实际的许可证对象; Semaphore只保留可用数量的计数,并相应地执行。
//线程数量,限流
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread(()->{
try {
// acquire() 得到
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//release() 释放
semaphore.release();
}
},String.valueOf(i)).start();
}
原理:
semaphore.acquire()
:获得,假设已经满了,等待被释放为止
semaphore.release():释放,会将信号量释放+1,唤醒等待的线程
读写锁(共享、排他)
Interface ReadWriteLock
- 读可以被多线程同时读,写的时候只能有一个线程去写
独占锁,排它锁==> 写锁
共享锁 ==> 读锁
- 特性
读-读 可以共存
读-写 不可共存
写-写 不可共存
代码展示
核心部分
readWriteLock.writeLock().lock();
readWriteLock.writeLock().unlock();
readWriteLock.readLock().lock();
readWriteLock.readLock().unlock();
class MyCacheWithLock{
private volatile Map<String,Object> map = new HashMap<>();
//读写锁,更加细粒度的控制
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 存,写的时候,只希望同时只有一个线程写
public void put(String key,Object value){
readWriteLock.writeLock().lock();
try{
System.out.println(Thread.currentThread().getName()+"写入===>"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入===>"+key+"===OK!!");
}catch(Exception e){
e.printStackTrace();
}finally {
readWriteLock.writeLock().unlock();
}
}
// 取,读,所有人都可以读
public void get(String key){
readWriteLock.readLock().lock();
try{
System.out.println(Thread.currentThread().getName()+"读取===>"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读取===>"+key+"===OK!!");
}catch(Exception e){
e.printStackTrace();
}finally {
readWriteLock.readLock().unlock();
}
}
}