1.并发、并行、吞吐量
并发:是指单位时间内(通常1s)系统或程序处理的请求数量。
并行:多个线程或进程同时运行完成某个大型计算的一部分。
吞吐量:网络吞吐量是指定单位时间内传输的数据字节大小,系统吞吐量与并发差不多一个意思。
2.锁
隐式锁:synchronized(同步关键字)
最优用法:
private byte[] lock = new byte[1];
public void myFunc() {
synchronized(lock){
//...
}
}
显示锁:Lock
显示锁与隐式锁的区别:
1.Lock只适用于代码块,synchronized对象之间互斥。
2.Lock锁粒度更细,因此线程的开销可能会更小一些,性能更佳,但需要手动开启并释放锁。
ReentrantLock:重入锁
是Lock接口的一个实现类。
final Lock lock = new ReentrantLock();
public void func(){
try{
lock.lock();
}catch(Exception e){
//...
}finally{
lock.unlock();
}
}
ReentrantReadWriteLock:读写锁
是ReadWriteLock接口唯一实现类,内部维护了一个读Lock和写Lock
基本使用示例:
public static void main(String[] args) {
DefaultContainer defaultContainer = new DefaultContainer();
for(int i = 1; i <= 4; i ++) {
new Thread(() ->{
defaultContainer.read();
}, "thread-read-" + i).start();
}
for(int i = 1; i <= 2; i ++) {
new Thread(() ->{
defaultContainer.write();
}, "thread-write-" + i).start();
}
try {
Thread.sleep(2100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static class DefaultContainer {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); // true表示公平性
public void write() {
try {
lock.writeLock().lock(); //上写锁
System.out.println(Thread.currentThread().getName() + "->writed start");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "->writed over");
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.writeLock().unlock();
}
}
public void read() {
try {
lock.readLock().lock(); //上读锁
System.out.println(Thread.currentThread().getName() + "->read start");
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "->read over");
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.readLock().unlock();
}
}
}
从示例中不难看出,读-读之间并行执行,读-写,写-写串行
读写锁特性:
1.公平性:即读-写,写-写根据阻塞时间长短来顺序获取锁,读-读之间没有锁竞争因此不存在该特性。
public ReentrantReadWriteLock(boolean fair) //该构造函数中可以指定是否使用公平策略。公平性吞吐量小于非公平性
2.重入性:允许按照顺序再次获取锁的特性,获取写锁后可以再次获取读锁,但是获取读锁后不可以再获取写锁。
3.锁降级:获取写锁后,再获取读锁,然后释放写锁,当前线程就由写变成了读,谓之锁降级。
4.锁升级:没有这个概念,读取锁无法升级到写入锁,因为获取写入锁之前需要释放所有的读、写锁。
5.重入数:读、写锁的最大数量分别是65535。
StampedLock:悲观、乐观锁(后续补充)
3.volatile(内存可见性)和重排序
volatile:可见性
这里注意一下,该关键字修饰的变量只满足内存可见性,不满足原子性,即适用于并发读,单个写的应用场景。
因此如果有并发写的场景,volatile也无法做到原子操作,还是需要加锁。
执令重排序:
private int a = 0;
private boolean flag = false;
public void write() throws InterruptedException {
a =1;
flag = true;
}
public int read() throws InterruptedException {
int result = 0;
if(flag) {
result = a*a;
}
return result;
}
如果a=1和flag=true的执行顺序重排,那么read()线程的执行结果会发生改变,使用volatile修饰a和flag即可防止重排序
4.ThreadLocal (当前线程副本)
应用场景:
当多个线程需要操作共享变量时,相互之间的操作又不能彼此影响时,使用ThreadLocal
ThreadLocal实现原理:
首先ThreadLocal会维护一个初始的共享变量
protected T initialValue();通过重写ThreadLocal的该方法可以初始化共享变量
线程中通过ThreadLocal对象的get方法可以获取到当前共享变量的一个副本。
查看Thread相关源码可知,每个线程thread中都维护一个ThreadLocalMap对象,ThreadLocalMap是Thread的一个静态内部类,结构类似Map的key-value结构,key就是threadLocal对象,value则是共享变量的一份拷贝。
在threadLocal.get()的时候,实际获取的是当前线程的一个ThreadLocalMap,如果该map存在,则直接返回,即可拿到value。如果不存在,则会new一个新的ThreadLocalMap并赋值给当前线程中的ThreadLocalMap对象,同时把初始的value存到ThreadLocalMap中,key则是threadLocal对象,然后再返回初始的value。
大致如下图:
ThreadLocal使用时注意问题:
如果是在线程池中的线程使用ThreadLocal,注意在当前线程最后一次使用完后要remove(),否则线程池可能会重复利用同一个线程,那么ThreadLocal就会保留之前的数据,导致数据混乱。
例如javaweb中tomcat中的http线程就是线程池创建出来的会复用,因此一定要记得remove。
5.atomic原子操作
基本类型:AtomicInteger、AtomicLong、AtomicBoolean
比较常用的就是上面几种基本类型的,常用于多线程环境下替代加锁,底层依赖于C实现的基于CPU的CAS操作(比较交换)
6.并发集合类
并发Map:ConcurrentHashMap 替代HashTable
并发List:CopyOnWriteArrayList 线程安全的ArrayList
并发Set:CopyOnWriteArraySet 线程安全的Set基于CopyOnWriteArrayList
7.阻塞队列BlockingQueue
常用于生产者消费者应用模型
插入方法 | 移除方法 | ||
boolean add(E e); | 队列满则抛异常 | boolean remove(Object o); | 队列空则抛异常 |
boolean offer(E e); | 立即返回(不常用) | E poll(); | 立即返回队头元素,队列空则返回null |
void put(E e); | 队列满一直阻塞 | E take(); | 队列空则一直阻塞 |
boolean offer(E e, long timeout, TimeUnit unit); | 队列满,则阻塞至超时返回false | E poll(long timeout, TimeUnit unit); | 队列空,阻塞至超时返回null |
* ArrayBlockingQueue基于数组:适用于读多写少
* LinkedBlockingQueue基于链表:适用于读<=写
* 以上比较是基于他们内部维护的队列模型,基于生产者消费者模型的实际开发中多数时候还是用LinkedBlockingQueue,
该队列内部基于读写锁处理多线程数据安全,而ArrayBlockingQueue内部读写都使用了同一个锁,此外使用LinkedBQ
时最好指定大小,防止内存溢出,通常情况下,队列里存的数据都是类似索引的小数据,因此实际中可能不指定大小。
* PriorityBlockingQueue基于数组:有序的阻塞队列
该队列可以按照某种顺序来存取元素,保证顺序的方式是,构造队列时加入一个比较器。
另外该队列只会阻塞消费者,因此当生产速率大于消费时,可能会内存溢出,并且该队列没有大小限制。
构造方法:PriorityBlockingQueue(int initialCapacity, Comparator<? super E> comparator);initialCapacity该参数是队列的
初始容量大小,并不是指队列的大小,comparator是一个比较器接口,如下构建一个初始大小1000,按降序排序的阻塞队列
使用示例:
public class PriorityBlockQueueDemo {
final static Random random = new Random(23);
public static void main(String[] args) {
int initialCapacity = 1000;
/*
* 此处构造队列时,传入Comparator
* 也可以Person类直接实现比较器接口,则构造队列时无需传入
*/
PriorityBlockingQueue<Person> priorityBlockingQueue = new PriorityBlockingQueue<Person>(initialCapacity, (t1, t2) ->{
return t1.age >= t2.age ? 1 : -1; //注意该接口的排序规则,此处为按年龄升序,降序1和-1调换
});
for(int i = 0; i < 100; i ++) {
int nextInt = random.nextInt(55);
Person person = new Person().setName("name" + i).setAge(nextInt);
priorityBlockingQueue.put(person);
}
System.out.println(priorityBlockingQueue.toString());
}
static class Person {
private int age;
private String name;
public int getAge() {
return age;
}
public Person setAge(int age) {
this.age = age;
return this;
}
public String getName() {
return name;
}
public Person setName(String name) {
this.name = name;
return this;
}
@Override
public String toString() {
return "{age: " + age + ", name: '" + name + "'}";
}
}
}
* DelayQueue<E extends Delayed> 延时获取队列
该队列是一个支持优先级(即有序,会按照入队元素的有效时间进行排序)及延时获取的队列。
该队列的put方法不阻塞,即该队列无界,因此需要注意内存溢出的可能性。
该队列中的元素必须实现Delayed extends Comparable<Delayed >接口的两个方法如下:
int compareTo(T o); //该方法需要对元素的有效时间进行升序排序,保证时间早的先出队
long getDelay(TimeUnit unit); //该接口返回的是 当前元素的有效时间与当前系统时间差,如果差为0,则队列poll(long timeout, TimeUnit unit)方法会获取到该元素,否则会阻塞timeout
DelayQueue使用场景如下:
1.缓存系统key的失效,可以将key及其有效时间放入DelayQueue,起一个线程循环获取DelayQueue中的超时key,并从缓存中 移除。
2.一次性的定时任务,可以将任务ID及任务的执行时间放入DelayQueue,超时则取出任务ID并执行任务。
DelayQueue缓存系统的使用示例如下:
public class DelayQueueDemo {
public static void main(String[] args) {
final MemoryCache memoryCache = new MemoryCache(10000);
System.out.println(memoryCache.size());
final Random random = new Random();
for (int i = 1; i <= 1000; i++) {
String key = "test" + i;
String value = key + "-value";
try {
memoryCache.put(key, value, 10 + random.nextInt(21), TimeUnit.SECONDS);
memoryCache.put(key + "b", value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 缓存模型
static class MemoryCache {
final byte[] lock = new byte[0];
final DelayQueue<Delayed> delayQueue = new DelayQueue<Delayed>();
final ConcurrentHashMap<String, Object> container = new ConcurrentHashMap<String, Object>();
private long size = Integer.MAX_VALUE;
public MemoryCache() {
this.init();
}
public MemoryCache(long size) {
this.size = size;
this.init();
}
public void init() {
new Thread(() -> {
while (true) {
try {
System.out.println(
"delayQueue.size=" + delayQueue.size() + ",container.size=" + container.size()); // 间隔1秒打印队列元素
Delayed delayed = delayQueue.poll(1, TimeUnit.SECONDS); // 队列为空则阻塞1秒
if (delayed != null) {
ClearMemoryCacheTask task = (ClearMemoryCacheTask) delayed;
String memoryCacheKey = task.getMemoryCacheKey();
container.remove(memoryCacheKey);
System.out.println("container.remove(" + memoryCacheKey + ")");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
public long size() {
return container.mappingCount();
}
// put时设置过期时间
public void put(String key, Object value, long timeout, TimeUnit timeUnit) throws Exception {
if(size() >= this.size) { //此处需校验是否超过指定的缓存的最大size
throw new RuntimeException("MemoryCache is exceed, key=" + key);
}
if (timeout > 0 && timeUnit != null) {
long sourceDuration = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(timeout, timeUnit);
delayQueue.put(new ClearMemoryCacheTask(key, sourceDuration));
}
container.put(key, value);
}
// 永不过期
public void put(String key, Object value) throws Exception {
put(key, value, -1, null);
}
public Object get(String key) {
return container.get(key);
}
private class ClearMemoryCacheTask implements Delayed {
private String memoryCacheKey;
private long sourceDuration;
public ClearMemoryCacheTask(String memoryCacheKey, long sourceDuration) {
this.memoryCacheKey = memoryCacheKey;
this.sourceDuration = sourceDuration;
}
public String getMemoryCacheKey() {
return memoryCacheKey;
}
/*
* 比较器接口:比较队列当中的ClearMemoryCacheTask的源过期时间
* 此处比较规则是:源过期时间小的排到队头,只有这样才可以保证队列按时间先后移除ClearMemoryCacheTask
*/
@Override
public int compareTo(Delayed o) {
ClearMemoryCacheTask task = (ClearMemoryCacheTask) o;
// 此处比较规则是大的往队尾排,1和-1调换则反之。
return this.sourceDuration > task.sourceDuration ? 1
: (this.sourceDuration < task.sourceDuration ? -1 : 0);
}
/*
* 该接口返回的值应该为:源过期时间 - 当期时间 (注意单位,此处使用的毫秒数)
* 该接口返回值为0时,才会从当期队列中获取到ClearMemoryCacheTask
*/
@Override
public long getDelay(TimeUnit unit) {
return this.sourceDuration - System.currentTimeMillis();
}
@Override
public String toString() {
return JSONObject.toJSONString(this);
}
}
}
}
* SynchronousQueue<T> 同步队列
该队列不存储数据。
公平模式,按照FIFO阻塞多余的生产者或消费者,非公平模式,可能导致某些生产者或消费者线程饥渴,默认采用非公平模式。
如果只是为了做生产者和消费者之间的数据传递,SynchronousQueue性能要优于LinkedBlockingQueue和ArrayBlockingQueue。
使用示例:
public class SynchronousQueueDemo {
public static void main(String[] args) {
SynchronousQueue<String> synchronousQueue = new SynchronousQueue<String>();
new Thread(new ProducerTask(synchronousQueue)).start();
for(int i = 0; i < 10; i ++) {
new Thread(new ConsumerTask(synchronousQueue), "t" + i).start();
}
}
static class ProducerTask implements Runnable {
private SynchronousQueue<String> synchronousQueue;
public ProducerTask(SynchronousQueue<String> synchronousQueue) {
this.synchronousQueue = synchronousQueue;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(10); //休眠是为了System.currentTimeMillis()不会重复
this.synchronousQueue.put((System.currentTimeMillis() + "").substring(8));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class ConsumerTask implements Runnable {
private SynchronousQueue<String> synchronousQueue;
public ConsumerTask(SynchronousQueue<String> synchronousQueue) {
this.synchronousQueue = synchronousQueue;
}
@Override
public void run() {
while (true) {
try {
String take = this.synchronousQueue.take();
print(take);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void print(String msg) throws InterruptedException {
Thread.sleep(500); //模拟消费耗时
System.out.println(Thread.currentThread().getName() + "->" + msg);
}
}
}
* LinkedBlockingDeque 双端链表阻塞队列(后续跟进)
* LinkedTransferQueue 链表传输队列(后续跟进)
8.同步计数器
1.CountDownLatch 线程倒计数器
应用于主线程等待多个子线程执行结果或完成的场景,类似于Future.get()方法,会阻塞线程池中对应线程。
常用API:
CountDownLatch(int count);//count表示倒计数的个数
void countDown();//子任务结束后调用该方法将线程数减1
void await();//阻塞至count个子任务执行结束
boolean await(long timeout, TimeUnit unit);阻塞至count个子任务执行结束或超时返回false
简单实用,示例如下:
public class CountDownLatchDemo {
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(3);
DefaultTask defaultTask = new DefaultTask(countDownLatch);
System.out.println("start");
for(int i = 1; i < 4; i ++) {
new Thread(defaultTask, "t-" + i).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end");
}
static class DefaultTask implements Runnable{
final CountDownLatch countDownLatch;
final Random random = new Random();
public DefaultTask(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
this.doSome();
} catch (InterruptedException e) {
e.printStackTrace();
}
this.countDownLatch.countDown();
}
private void doSome() throws InterruptedException {
String name = Thread.currentThread().getName();
System.out.println(name + "doSome...start");
long millis = 500 + random.nextInt(500);
Thread.sleep(millis);
System.out.println(name + "doSome...end," + "耗时:" + millis);
}
}
}
2.Semaphore 信号量
应用场景:多个线程需要排队执行,并且可以同时执行多个线程,比如10个线程,每次都执行其中的5个,可以起到限流的效果。
常用API:
Semaphore(int permits);//构造信号量,并初始化信号个数permits。
Semaphore(int permits, boolean fair);//fair=true设置信号量为公平策略,公平策略下,线程会被依次放入一个FIFO队列,依次,默认false或不设置则为非公平策略。
void acquire();//从Semaphore当中获取一个许可,此刻如果许可已全部使用,则阻塞到某个线程释放出一个许可为止。
boolean tryAcquire(long timeout, TimeUnit unit);//在获取到一个许可前阻塞timeout时间,则返回false,否则获取到一个许可,返回true。
boolean tryAcquire(int permits, long timeout, TimeUnit unit);同上,获取多个许可
int availablePermits();//返回可用的许可个数
int drainPermits();//获取Semaphore的所有许可,返回值为当前获取到的许可个数,该方法的意义在于两点:一,返回当前可用的许可个数;二,将Semaphore的的许可清空,即Semaphore不可用直到释放其中的一个或多个许可。
protected void reducePermits(int reduction);//动态减少许可数,子类重写该方法,可调用(待后续跟进)
void release();//释放一个许可
void release(int permits);//释放给定数目的许可
boolean isFair();//返回Semaphore的公平策略,true表示公平
int getQueueLength();//返回等待获取许可的线程个数
简单示例:
public class SemaphoreDemo {
volatile static int total = 0;
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(5, false);
for(int i = 1; i < 51; i ++) {
new Thread(() ->{
int count = 0;
while(true) {
try {
semaphore.acquire();
count ++;
total ++;
/*
* 分别设置Semaphore为公平、非公平策略,观察count的变化,可以很明显看出公平模式下,会按照线程先后次序入队
*/
System.out.println(Thread.currentThread().getName() + "-拿到了信号量,并开始执行第-" + count + "-次" + ",total=" + total);
Thread.sleep(1000);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t" + i).start();
}
}
}
3.CyclicBarrier 循环屏障
* 允许一组相同task的线程执行到某个点时,彼此相互等待,当所有线程都到达这个点时,再并行向下执行。
* CountDownLatch是主线程等待各个子线程,而CyclicBarrier是各个子线程相互等待,而主线程并不等待。
* CountDownLatch只能使用一次,而CyclicBarrier可以在各个子线程到达后,调用reset()方法重置,再次调用await设置等待点
常用API:
CyclicBarrier(int parties);//parties为构造时,指定的屏障线程个数,一经设置,无法改变。
CyclicBarrier(int parties, Runnable barrierAction);//barrierAction这是一个回调线程task,子线程都到达await点后,会回调。
int await();//设置一个屏障点,返回值为当前子线程到达屏障点的顺序索引,0表示最后一个到达,parties-1表示第一个到达。
int await(long timeout, TimeUnit unit);//同上,在timeout时间内如果有线程还未到达,则抛出超时异常。
int getNumberWaiting();//返回已经到达屏障等待的子线程个数。
int getParties();//返回构造时指定的屏障线程个数parties常量。
void reset();//重置屏障,可以再次调用await()方法设置屏障。
示例:模拟如下场景,来运行一个程序
大家一起开始去打饭
Marry:打饭结束,开始等待其他人...
Lucy:打饭结束,开始等待其他人...
jack:打饭结束,开始等待其他人...
所有人打饭结束,大家一起开始吃午饭
Lucy:已吃完饭,开始等待其他人...
Marry:已吃完饭,开始等待其他人...
jack:已吃完饭,开始等待其他人...
所有人已吃饭结束
默认实现:
public class CyclicBarrierDemo {
static class HaveLunchTask implements Runnable {
private CyclicBarrier cyclicBarrier;
public HaveLunchTask(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
// 打饭逻辑
private void takeMeals(String personName) throws InterruptedException {
Thread.sleep((1 + new Random().nextInt(5)) * 1000);
System.out.println(personName + ":打饭结束,开始等待其他人...");
}
// 吃饭逻辑
private void haveLunch(String personName) throws InterruptedException {
Thread.sleep((1 + new Random().nextInt(5)) * 1000);
System.out.println(personName + ":已吃完饭,开始等待其他人...");
}
@Override
public void run() {
try {
String personName = Thread.currentThread().getName();
// 开始打饭
takeMeals(personName);
// 等待其他人打饭结束
cyclicBarrier.await();
// 开始吃饭
haveLunch(personName);
// 等待其他人吃饭结束
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {
private int count = 0; //标记回调线程执行的次数
//回调线程
@Override
public void run() {
count++;
process(count);
}
//根据state判断是第几次到达屏障,来执行不同的逻辑
private void process(int state) {
switch (state) {
case 1:
System.out.println("\r\n所有人打饭结束,大家一起开始吃午饭\r\n");
break;
case 2:
System.out.println("\r\n所有人已吃饭结束");
break;
default:
break;
}
}
});
ArrayList<String> list = new ArrayList<String>();
list.add("Marry");
list.add("jack");
list.add("Lucy");
//程序起点
System.out.println("大家一起开始去打饭\r\n");
for (int i = 0; i < list.size(); i++) {
new Thread(new HaveLunchTask(cyclicBarrier), list.get(i)).start();
}
}
}
实现二:不使用CyclicBarrier构造回调线程,手动启动一个监控线程
package cn.qu.mv.concurrent;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicInteger;
public class CyclicBarrierDemo {
/**
* 该类继承CyclicBarrier
* 并增加一个方法
* boolean awaitForEndAll() 是否所有CyclicBarrier屏障的线程已经执行完毕, 如果atomic不小于parties, 则认为CyclicBarrier-> 所有相关线程await完毕。
* 复写一个方法
* int await() 在访问完父类CyclicBarrier的await()方法后,对atomic++
* @author vn03jxv
*
*/
static class MyCyclicBarrier extends CyclicBarrier {
private final byte[] lock = new byte[0];
AtomicInteger atomicInteger = new AtomicInteger();
public MyCyclicBarrier(int parties) {
super(parties);
this.atomicInteger.set(0);
}
public MyCyclicBarrier(int parties, Runnable barrierAction) {
super(parties, barrierAction);
this.atomicInteger.set(0);
}
public int await() throws InterruptedException, BrokenBarrierException {
int await = super.await();
this.atomicInteger.incrementAndGet();
return await;
}
public boolean awaitForEndAll() {
if (this.atomicInteger.get() >= super.getParties()) {
synchronized (lock) {
if (this.atomicInteger.get() >= super.getParties()) {
this.atomicInteger.set(0);
return true;
}
}
}
return false;
}
}
static class HaveLunchTask implements Runnable {
private CyclicBarrier cyclicBarrier;
public HaveLunchTask(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
// 打饭逻辑
private void takeMeals(String personName) throws InterruptedException {
Thread.sleep((1 + new Random().nextInt(5)) * 1000);
System.out.println(personName + ":打饭结束,开始等待其他人...");
}
// 吃饭逻辑
private void haveLunch(String personName) throws InterruptedException {
Thread.sleep((1 + new Random().nextInt(5)) * 1000);
System.out.println(personName + ":已吃完饭,开始等待其他人...");
}
@Override
public void run() {
try {
String personName = Thread.currentThread().getName();
// 开始打饭
takeMeals(personName);
// 等待其他人打饭结束
cyclicBarrier.await();
// 开始吃饭
haveLunch(personName);
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
/*main方法正常输出如下:
大家一起开始去打饭
Marry:打饭结束,开始等待其他人...
Lucy:打饭结束,开始等待其他人...
jack:打饭结束,开始等待其他人...
所有人打饭结束,大家一起开始吃午饭
Lucy:已吃完饭,开始等待其他人...
Marry:已吃完饭,开始等待其他人...
jack:已吃完饭,开始等待其他人...
所有人已吃饭结束
*/
public static void main(String[] args) {
MyCyclicBarrier cyclicBarrier = new MyCyclicBarrier(3);
ArrayList<String> list = new ArrayList<String>();
list.add("Marry");
list.add("jack");
list.add("Lucy");
//2.启动一个监控线程
new Thread(() -> {
while (!cyclicBarrier.awaitForEndAll()) { //当Marry、jack、Lucy三个人都打饭完成,则程序向下执行
}
System.out.println("\r\n所有人打饭结束,大家一起开始吃午饭\r\n");
cyclicBarrier.reset(); //重置cyclicBarrier,以便复用
while (!cyclicBarrier.awaitForEndAll()) { //当Marry、jack、Lucy三个人都吃饭结束,则程序向下执行
}
System.out.println("\r\n所有人已吃饭结束");
}).start();
//1.程序起点
System.out.println("大家一起开始去打饭\r\n");
for (int i = 0; i < list.size(); i++) {
new Thread(new HaveLunchTask(cyclicBarrier), list.get(i)).start();
}
}
}
4.AbstractQueuedSynchronizer抽象队列同步器(以下简称AQS)
* aqs是java.util.concurrent包下的核心开发组件,用于构建多线程间的锁或其他相关同步器的基础框架,底层基于一个FIFO队列来维护各个线程。
* aqs提供了两种模式:排他模式和共享模式。
排他模式:其他线程获取锁将无法成功
共享模式:其他线程获取锁可能会成功
* aqs是以一个int类型state来维护同步状态,当state为0时表示未占用,可以获取锁,当state为1时表示占用。
并为该状态提供以下几个API供使用:
final int getState();
final void setState(int newState);
final boolean compareAndSetState(int expect, int update);//检查当前状态,设置新的状态
final void setExclusiveOwnerThread(Thread thread);//设置独占线程,当释放锁时,应设置为null
* aqs是一个抽象类,通常使用其实现一个自定义的同步器的时候,都是在自定义同步器的内部实现一个AbstractQueuedSynchronizer的子类,并复写以下方法:
boolean isHeldExclusively();//是否处于占用状态
boolean tryAcquire(int arg);//当状态为0时获取锁
boolean tryRelease(int arg);//释放锁,将状态置为0
Condition newCondition();//new一个Condition的实现,比如:java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject
* 关于AQS的用法可以参考java.util.concurrent.locks.ReentrantLock该类,或者基于AQS实现一个简单的lock
public class AQSDemo {
public static void main(String[] args) {
Lock lock = new DefaultLock();
new Thread(() -> {
while(true) {
try {
Thread.sleep(1000);
lock.lock();
System.out.print(Thread.currentThread().getName() + "--> print-");
for(int i = 0; i < 10; i ++) {
System.out.print(i);
}
System.out.println();
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}, "producer").start();
new Thread(() -> {
while(true) {
try {
Thread.sleep(1000);
lock.lock();
System.out.print(Thread.currentThread().getName() + "--> print-");
for(int i = 0; i < 10; i ++) {
System.out.print(i);
}
System.out.println();
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}, "consumer").start();
}
static class DefaultLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 2219105556300329463L;
private final Sync sync = new Sync();
// 实现AQS的内部类
private static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 6931334064340153098L;
// 返回是否被占用
protected boolean isHeldExclusively() {
return getState() == 1;
}
// 如果state是0,则获取锁,并立即返回true
public boolean tryAcquire(int acquires) {
assert acquires == 1; // Otherwise unused
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 释放锁,并把state置为0,设置exclusiveOwnerThread独占线程为null
protected boolean tryRelease(int releases) {
assert releases == 1; // Otherwise unused
if (getState() == 0)
throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// Provides a Condition
Condition newCondition() {
return new ConditionObject();
}
}
@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.convert(time, unit));
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
}
}
9.线程池
线程池总结 -> 24.