该栏目讲叙多线程基础、共享模型的内存、管程、无锁、不可变设计和多线程并发工具
文章目录
AQS 原理
概述
:全称 AbstractQueuedSynchronizer,该同步器利用了一个 int 来表示状态,可以用于构建锁或者其他相关同步装置的基础框架特点
- 用 state 属性来表示资源的状态(独占模式和共享模式)
- 提供了基于 FIFO 的等待队列,类似于 Monitor 的 EntryList
- 可以使用条件变量来实现等待唤醒机制,支持多个条件变量,类似于 Monitor 的 WaitSet
自定义锁
/**
* 自定义不可重入锁
*/
public class NonReentrantLock implements Lock {
private final Sync sync = new Sync();
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public void unlock() {
sync.release(0);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
/**
* 同步器
*/
static class Sync extends AbstractQueuedSynchronizer {
/**
* 尝试加锁
*
* @param arg 参数
* @return 加锁成功返回true,否则返回false
*/
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
/**
* 测试解锁
*
* @param arg 参数
* @return 解锁成功返回true,否则返回false
*/
@Override
protected boolean tryRelease(int arg) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
/**
* 是否是独占锁
*
* @return true表示是,否则表示否
*/
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
/**
* 获取条件变量
*
* @return 条件变量
*/
public Condition newCondition() {
return new ConditionObject();
}
}
}
读写锁
1、ReentrantReadWriteLock
概述
:当读操作远远高于写操作时,这时候使用读写锁
让读-读可以并发,提高性能特点
- 读锁不支持条件变量
- 重入时升级不支持,即持有读锁的情况下去获取写锁,会导致获取写锁永久等待
- 重入时降级支持:即持有写锁的情况下去获取读锁
案例
/**
* 测试类
*/
public class ReentrantReadWriteLockDemo {
public static void main(String[] args) {
DataContainer dataContainer = new DataContainer();
// 测试 读 - 写
new Thread(() -> {
dataContainer.write("Hello");
}, "WriteThread").start();
new Thread(() -> {
Object data = dataContainer.read();
}, "ReadThread").start();
}
}
/**
* 数据容器 - 读写锁
*/
class DataContainer {
private Object data;
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
private ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
public Object read() {
readLock.lock();
try {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("读取数据:" + data);
return data;
} finally {
System.out.println("释放读锁");
readLock.unlock();
}
}
public void write(Object data) {
writeLock.lock();
try {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("写入数据:" + data);
this.data = data;
} finally {
System.out.println("释放写锁");
writeLock.unlock();
}
}
}
2、StampedLock
概述
:在使用读锁、写锁时都必须配合【戳】使用,支持乐观读,可避免加锁缺点
- 不支持条件变量
- 不支持可重入
案例
/**
* 测试类
*/
public class StampedLockDemo {
public static void main(String[] args) {
DataContainer container = new DataContainer(10);
// 写入数据
new Thread(() -> container.write(100), "WriteThread").start();
// 读取数据
new Thread(container::read, "ReadThread").start();
}
/**
* 数据容器类
*/
static class DataContainer {
private int data;
private StampedLock stampedLock = new StampedLock();
public DataContainer(int data) {
this.data = data;
}
/**
* 写入数据
*
* @param data 数据
*/
public void write(int data) {
long stamped = stampedLock.writeLock();
try {
try {
System.out.println("等待写入数据...");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("写入数据:" + data);
this.data = data;
} finally {
stampedLock.unlock(stamped);
System.out.println("释放写锁");
}
}
public int read() {
long stamped = stampedLock.tryOptimisticRead();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (stampedLock.validate(stamped)) {
System.out.println("乐观读情况下读取成功:" + data);
return data;
}
System.out.println("乐观读失败,升级为读锁");
try {
stamped = stampedLock.readLock();
System.out.println("读锁情况下读取成功:" + data);
return data;
} finally {
stampedLock.unlock(stamped);
}
}
}
}
Semaphore
概述
:信号量,用来限制能同时访问共享资源的线程上限案例
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
// 获取许可
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "开始运行");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "运行结束");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
// 释放许可
semaphore.release();
}
}, "T" + i).start();
}
}
}
CountdownLatch
概述
:用来进行线程同步协作,等待所有线程完成倒计时常用方法
- await():用来等待计数归零
- countDown():用来让计数减一
案例
/**
* 模拟游戏玩家加载
*/
public class CountDownLatchDemo {
public static void main(String[] args) {
final ExecutorService pool = Executors.newFixedThreadPool(10);
CountDownLatch countDownLatch = new CountDownLatch(10);
Random random = new Random();
String[] players = new String[10];
System.out.println("游戏加载中...");
for (int i = 0; i < 10; i++) {
int index = i;
pool.submit(() -> {
for (int j = 0; j < 100; j++) {
try {
Thread.sleep(random.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
players[index] = j + "%";
System.out.print("\r" + Arrays.toString(players));
}
countDownLatch.countDown();
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("\n游戏开始...");
// 关闭线程池
pool.shutdown();
}
}
CyclicBarrier
概述
:循环栅栏,用来进行线程协作,等待线程满足某个计数案例
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(2, () -> {
System.out.println("T1线程,T2线程执行任务完毕");
});
new Thread(() -> {
try {
System.out.println("开始执行任务1...");
barrier.await();
System.out.println("任务1执行结束...");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}, "T1").start();
new Thread(() -> {
try {
System.out.println("开始执行任务2...");
TimeUnit.SECONDS.sleep(2);
System.out.println("任务2执行结束...");
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}, "T2").start();
}
}
线程安全集合类
1、ConcurrentHashMap
概述
:一个线程安全的、支持高效并发的容器,可以支持 16 个线程执行并发写操作及任意数量线程的读操作案例
public class ConcurrentHashMapDemo {
public static void main(String[] args) {
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
// 存储数据
for (int i = 0; i < 10; i++) {
map.put("K" + i, "V" + i);
}
// 删除数据
String value = map.remove("K1");
System.out.println("删除的元素值:" + value);
// 获取集合大小
final int size = map.size();
System.out.println("size = " + size);
// 根据键获取值
value = map.get("K2");
System.out.println("获取元素值:" + value);
// 遍历集合
map.forEach((k, v) -> System.out.println(k + "=" + v));
// 获取集合状态
final boolean empty = map.isEmpty();
System.out.println("集合是否为空:" + empty);
}
}
2、ConcurrentLinkedQueue
概述
:一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序案例
public class ConcurrentLinkedQueueDemo {
public static void main(String[] args) {
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
// 添加元素
queue.add("First");
queue.add("Second");
queue.add("Third");
queue.offer("Last"); // 从队列尾部添加
// 删除元素
final boolean result = queue.remove("First");
System.out.println("删除结果:" + result);
// 获取元素大小
final int size = queue.size();
System.out.println("元素大小:" + size);
// 获取元素,但不删除
String value = queue.peek();
System.out.println("获取元素值:" + value);
// 获取并删除
value = queue.poll();
System.out.println("获取元素值:" + value);
// 是否为空
final boolean empty = queue.isEmpty();
System.out.println("队列是否为空:" + empty);
// 是否包含
final boolean flag = queue.contains("Second");
System.out.println("队列是否包含Second:" + flag);
// 遍历
queue.forEach(v -> System.out.println("元素值:" + v));
}
}
3、CopyOnWriteArrayList
概述
:底层实现采用了写入时拷贝
的思想,增删改操作会将底层数组拷贝一份,更改操作在新数组上执行,这时不影响其它线程的并发读,读写分离缺点
- Get 弱一致性
- 迭代器弱一致性
案例
public class CopyOnWriteArrayListDemo {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// 添加元素
for (int i = 0; i < 10; i++) {
list.add(i, "Value" + i);
}
// 删除元素
String value = list.remove(0);
System.out.println("删除元素:" + value);
// 清空元素
// list.clear();
// 修改元素
list.set(1, "Java");
// 获取元素
value = list.get(2);
System.out.println("获取元素:" + value);
// 是否为空
final boolean empty = list.isEmpty();
System.out.println("集合是否为空:" + empty);
// 是否包含
final boolean contains = list.contains("Value3");
System.out.println("元素是否包含Value3:" + contains);
// 遍历
list.forEach(v -> System.out.println("元素值:" + v));
}
}