目录
01,常用的辅助类
- 先在我们来说一说
java.util.concurrent
包下的三个常用辅助类
java.util.concurrent.CountDownLatch
CountDownLatch:计数器(下计数器):
允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助
在具体实现的时候,线程执行的速度不同,有的快有的慢,部分业务需要等待其他业务执行完毕才能结束
本次演示:创建六个线程完毕之后,再结束等待,执行后面拿的操作
public class CountDownLatchTest01 {
public static void main(String[] args) throws Exception {
CountDownLatch count=new CountDownLatch(6);//减法计数器,减完执行操作
for(int i=0;i<6;i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName()+":OK!!!");
count.countDown();//执行减一操作
},String.valueOf(i)).start();
}
count.await();//等待,直到减为0
System.out.println("End");
}
}
java.util.concurrent.CyclicBarrier
- CyclicBarrier:栅栏类,类似于屏障
如果达不到给定的线程数量,将会等待,如果达到给定的线程数量将会跳越屏障
跳跃屏障后可以设定要进行的操作,也可以不设定,这个由重载的构造器决定
从调用这个方法处:cyclicBarrier.await();开始阻塞
注意:如果线程数达不到要求,就会一直等待,陷入阻塞
public class CyclicBarrierTest {
public static int num=0;
public static void main(String[] args) {
CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{
System.out.println("七个线程创建完毕。。。。");
});
for(int i=1;i<8;i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"条线程。。。");
//需要调用await()进行等待
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}finally {
System.out.println(++num);
}
},String.valueOf(i)).start();
}
}
}
java.util.concurrent.Semaphore
- Semaphore:信号量
用来限制流量,每次只能有这么多线程,这些线程不释放,其他线程就进不来
public class SemaphoreTest {
public static void main(String[] args) {
//线程数量
Semaphore semaphore=new Semaphore(3);
for(int i=0;i<6;i++){
new Thread(()->{
//acquire(),【得到】
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"线程抢到执行权。。。");
TimeUnit.SECONDS.sleep(4);//睡四秒
System.out.println(Thread.currentThread().getName()+"线程释放执行权。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//release(),【释放】
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
02,java.util.concurrent.locks.ReadWriteLock:读写锁
- 读写锁是
java.util.concurrent.locks
包下的一个接口 - 该接口只有一个实现类:
ReentrantReadWriteLock
(可重入读写锁)
先看看不加锁
的情形
public class ReadWriteLockTest01 {
public static void main(String[] args) {
MyCache myCache = new MyCache();
// 五个线程写
for (int i = 1; i < 5; i++) {
final int temp = i;
new Thread(() -> {
myCache.put("write_" + temp, "write_" + temp);
}, "write_" + temp).start();
}
// 五个线程读
for (int i = 1; i < 5; i++) {
final int temp = i;
new Thread(() -> {
myCache.get("write_" + temp);
}, "read_" + temp).start();
}
}
}
/*自定义缓存:不带锁*/
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
public void put(String key, Object value) {
try {
System.out.println(Thread.currentThread().getName() + " 写 " + key);
Thread.sleep(2000); // 加一个2s的延时
map.put(key, value); // 存(写)
System.out.println(Thread.currentThread().getName() + " 写结束");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public void get(String key) {
try {
System.out.println(Thread.currentThread().getName() + " 开始读 " + key);
String value= (String) map.get(key);// 取(读)
System.out.println(Thread.currentThread().getName() + " 读结束 " + value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
输出结果:
write_1 写 write_1
write_2 写 write_2
write_4 写 write_4
write_3 写 write_3
read_1 开始读 write_1
read_1 读结束 null
read_2 开始读 write_2
read_2 读结束 null
read_3 开始读 write_3
read_4 开始读 write_4
read_3 读结束 null
read_4 读结束 null
write_4 写结束
write_2 写结束
write_1 写结束
write_3 写结束
上面的代码会出现的问题:
没有任何顺序可言,写的时候可以读,读的时候也可以写,感觉很乱。
加锁
的情形
读写锁的【读锁】可以有多个线程同时读,【写锁】写的时候只能有一个线程来写,类似共享锁和独占锁
- 共享锁:读锁
- 独占锁:写锁
读-读 可以共存!
读-写 不能共存!
写-写 不能共存!
public class ReadWriteLockTest01 {
public static void main(String[] args) {
MyCache myCache = new MyCache();
// 五个线程写
for (int i = 1; i < 5; i++) {
final int temp = i;
new Thread(() -> {
myCache.put("write_" + temp, "write_" + temp);
}, "write_" + temp).start();
}
// 五个线程读
for (int i = 1; i < 5; i++) {
final int temp = i;
new Thread(() -> {
myCache.get("write_" + temp);
}, "read_" + temp).start();
}
}
}
/*自定义缓存:不带锁*/
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void put(String key, Object value) {
readWriteLock.writeLock().lock(); // 加一个读锁(独占的)
try {
System.out.println(Thread.currentThread().getName() + " 写 " + key);
Thread.sleep(2000); // 加一个2s的延时
map.put(key, value); // 存(写)
System.out.println(Thread.currentThread().getName() + " 写结束");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
readWriteLock.writeLock().unlock(); // 解锁
}
}
public void get(String key) {
readWriteLock.readLock().lock(); // 加一个读锁(共享的)
try {
System.out.println(Thread.currentThread().getName() + " 开始读 " + key);
String value = (String) map.get(key);// 取(读)
System.out.println(Thread.currentThread().getName() + " 读结束 " + value);
} catch (Exception e) {
throw new RuntimeException(e); // 解锁
}
}
}
运行结果:
write_1 写 write_1
write_1 写结束
write_2 写 write_2
write_2 写结束
write_3 写 write_3
write_3 写结束
write_4 写 write_4
write_4 写结束
read_1 开始读 write_1
read_1 读结束 write_1
read_2 开始读 write_2
read_2 读结束 write_2
read_3 开始读 write_3
read_3 读结束 write_3
read_4 开始读 write_4
read_4 读结束 write_4
分析:
读的时候可以插队,因为怎样读都行。 读-读 可以共存!
写的时候不能插队,必须等别人写完你才可以写。 写-写 不能共存!
并且写的时候也是不能读的。 读-写 不能共存!
03,java.util.concurrent.BlockingQueue:阻塞对列
- BlockingQueue(继承了Collection接口):阻塞队列,
java.util.concurrent.BlockingQueue
接口
队列:FIFO,先进先出
阻塞:当队列满了就阻塞了,前面没出去,后面进不来
取:当队里里空的时候,阻塞,等待生产
存:当队列里满的时候,阻塞,等待消费
常用
实现类:
ArrayBlockQueue:数组阻塞队列
LinkedBlockQueue:链表阻塞对列
SynchronousQueue:同步阻塞对列
- 操作阻塞对列的四组API
对队列的操作只有 存 和 取,但是阻塞队列有四组不同的API
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer | put | offer |
移除 | remove | poll | take | poll |
检查队首元素 | element | peek |
ArrayBlockingQueue:
public class BlockQueueTest01 {
public static void main(String[] args) {
test01();
}
/**
* 抛出异常
*/
public static void test01(){
BlockingQueue blockingQueue=new ArrayBlockingQueue(3);//队列大小为3
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
//System.out.println(blockingQueue.add("d"));//报错
System.out.println("===========================");
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//System.out.println(blockingQueue.remove());//报错
}
/**
* 有返回值,没有异常
*/
public static void test2(){
// 队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
// System.out.println(blockingQueue.offer("d")); // false 不抛出异常!
System.out.println("============================");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll()); // null 不抛出异常!
}
/**
* 等待,阻塞(一直阻塞)
* */
public static void test3() throws InterruptedException {
// 队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
// 一直阻塞
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
// blockingQueue.put("d"); // 队列没有位置了,一直阻塞
System.out.println("======================");
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
//System.out.println(blockingQueue.take()); // 没有这个元素,一直阻塞
}
/**
* 等待,阻塞(等待超时)
* */
public static void test4() throws InterruptedException {
// 队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.offer("a");
blockingQueue.offer("b");
blockingQueue.offer("c");
// blockingQueue.offer("d",2,TimeUnit.SECONDS); // 等待超过2秒就退出
System.out.println("===============");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
//blockingQueue.poll(2,TimeUnit.SECONDS); // 等待超过2秒就退出
}
}
synchronousQueue:
synchronousQueue:同步队列
没有容量:进去一个元素,必须等待取出来之后,才能再往里面放一个元素
和其他的BlockingQueue 不一样, SynchronousQueue 不存储元素
put了一个元素,必须从里面先take取出来,否则不能在put进去值
存取方法:put、take
public class SynchronousQueueTest02 {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new SynchronousQueue<>(); // 同步队列
//存
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+" put 1");
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName()+" put 2");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName()+" put 3");
blockingQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T1").start();
//取
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+" take=>"+blockingQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+" take=>"+blockingQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+" take=>"+blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T2").start();
}
}