1、什么是JUC
java.util.concurrent在并发编程中使用的工具类
进程/线程回顾
https://www.cnblogs.com/minmin123/p/11411321.html
2、进程/线程回顾
进程:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。 一个程序,QQ.exe Music.exe 程序的集合,一个进程往往可以包含多个线程,至少包含一个!
线程:通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。
java默认由几个线程? 2个 main,GC
java真的可以开启线程吗? 开不了,只能通过native 本地方法调用底层的C++操作硬件。
并发、并行
并发:同一时刻多个线程在访问同一个资源,多个线程对一个点 例子:小米9今天上午10点,限量抢购 春运抢票 电商秒杀...
- CPU一核,模拟出来多条线程,天下武功,唯快不破,快速交替
并行:多项工作一起执行,之后再汇总 例子:泡方便面,电水壶烧水,一边撕调料倒入桶中
- CPU多核,多个线程可以同时执行;
并发编程的本质:充分利用CPU的资源
线程状态
Thread.State
public enum State {
NEW ,(新建)
RUNNABLE ,(运行)
BLOCKED ,(阻塞)
WAITING ,(等待)
TIMED_WAITING ,(超时等待,过时不候)
TERMINATED ;(终结)
}
Java线程状态:
1. 新建状态(New): 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
3. 运行状态(Running): 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
4. 阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
(02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
(03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
sleep和wait的区别
1,这两个方法来自不同的类分别是Thread和Object
2,最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
3,wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在
任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
4,sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
3、Lock锁(重点)
回顾synchronized
package com.panghl.juc.demo01;
/**
* @Author panghl
* @Date 2021/5/23 18:02
* @Description 基本的卖票例子
* 真正的多线程开发,公司中的开发
* 线程就是一个单独的资源类,没有任何附属的操作
* 1、属性、方法
**/
public class SaleTicketDemo01 {
public static void main(String[] args) {
//并发:多线程操作同一个资源类
Ticket ticket = new Ticket();
new Thread(() -> {
for (int i = 0; i < 100; i++) {
ticket.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 60; i++) {
ticket.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 60; i++) {
ticket.sale();
}
}, "C").start();
}
}
//资源类 OOP
class Ticket {
//属性、方法
private int number = 100;
//卖票的方式
// synchronized 本质:队列,锁
public synchronized void sale() {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + "票,剩余=>" + number);
}
}
}
Lock接口
公平锁:十分公平,可以先来后到。
非公平锁:十分不公平,可以插队(默认)。
package com.panghl.juc.demo01;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author panghl
* @Date 2021/5/23 18:02
* @Description 基本的卖票例子
* 真正的多线程开发,公司中的开发
* 线程就是一个单独的资源类,没有任何附属的操作
* 1、属性、方法
**/
public class SaleTicketDemo02 {
public static void main(String[] args) {
//并发:多线程操作同一个资源类
Ticket2 ticket = new Ticket2();
new Thread(() -> {
for (int i = 0; i < 100; i++) {
ticket.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 60; i++) {
ticket.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 60; i++) {
ticket.sale();
}
}, "C").start();
}
}
/**
* Lock 三部曲
* 1、new ReentrantLock()
* 2、lock.lock() //加锁
* 3、finally {lock.unlock();} //释放锁
*/
class Ticket2 {
//属性、方法
private int number = 100;
Lock lock = new ReentrantLock();
//卖票的方式
public void sale() {
lock.lock(); //加锁
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + "票,剩余=>" + number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
synchronized 和 Lock 锁的区别
1、lock是一个接口,而synchronized是java的一个关键字。
2、synchronized在发生异常时会自动释放占有的锁,因此不会出现死锁;而lock发生异常时,不会主动释放占有的锁,必须手动来释放锁,可能引起死锁的发生
3、synchronized无法判断获取锁的状态,Lock 可以判断是否获取到了锁。
4、synchronized线程1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁不一定会等待。
5、synchronized 可重入锁,不可以中断的,非公平;Lock,可重入锁,可以 判断锁,非公平(可以自己配置)
6、synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码!
锁是什么,如何判断锁的是谁?
https://blog.csdn.net/weixin_40927436/article/details/105167386
4、生产者和消费者问题
生产者和消费者问题 Synchronized版
package com.panghl.juc.pc;
/**
* @Author panghl
* @Date 2021/5/23 18:57
* @Description 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
* 线程交替执行 A B 操作同一个变量 num = 0
* A num+1
* B num-1
**/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
}
}
//数字 资源类 --- 判断等待,业务,通知
class Data {
private int number = 0;
public synchronized void increment() throws InterruptedException {
if (number != 0) {
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "==>" + number);
//通知其他线程,我+1完毕了!
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
if (number == 0) {
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "==>" + number);
//通知其他线程,我-1完毕了
this.notifyAll();
}
}
问题存在,A B C D 4个线程!---》不安全,虚假唤醒
什么是虚假唤醒:当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功
if 改为 while 判断
package com.panghl.juc.pc;
/**
* @Author panghl
* @Date 2021/5/23 18:57
* @Description 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
* 线程交替执行 A B 操作同一个变量 num = 0
* A num+1
* B num-1
**/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
//数字 资源类 --- 判断等待,业务,通知
class Data {
private int number = 0;
public synchronized void increment() throws InterruptedException {
while (number != 0) { //防止虚假唤醒
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "==>" + number);
//通知其他线程,我+1完毕了!
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (number == 0) {
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "==>" + number);
//通知其他线程,我-1完毕了
this.notifyAll();
}
}
为什么 if会出现虚假唤醒
因为if只会执行一次,执行完会接着向下执行if()外边的 而while不会,直到条件满足才会向下执行while()外边的
JUC版的生产者和消费者问题
通过Lock找到Condition
代码实现
package com.panghl.juc.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class B {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
//数字 资源类 --- 判断等待,业务,通知
class Data2 {
private int number = 0;
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
// private final Condition notEmpty = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0) { //防止虚假唤醒
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "==>" + number);
//通知其他线程,我+1完毕了!
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (number == 0) {
//等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "==>" + number);
//通知其他线程,我-1完毕了
condition.signal();
} catch (
Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
可以发现使用了Condition实现的效果和上面synchronized实现一样。
目前的问题:随机的状态,想要它编程有序的执行 A-》B-》C-》D
package com.panghl.juc.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author panghl
* @Date 2021/5/23 19:35
* @Description A执行完调用B B执行完调用C C执行完调用A
**/
public class C {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data3.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data3.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data3.printC();
}
}, "C").start();
}
}
class Data3 { //资源类 Lock
private final Lock lock = new ReentrantLock();
private final Condition condition1 = lock.newCondition();
private final Condition condition2 = lock.newCondition();
private final Condition condition3 = lock.newCondition();
private int number = 1;
public void printA() {
lock.lock();
try {
//业务,判断-》执行-》通知
while (number != 1) {
//等待
condition1.await();
}
System.out.println("打印A");
number = 2;
//唤醒,唤醒指定的人
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
;
public void printB() {
lock.lock();
try {
while (number != 2) {
//等待
condition2.await();
}
System.out.println("打印B");
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
;
public void printC() {
lock.lock();
try {
while (number != 3) {
//等待
condition3.await();
}
System.out.println("打印C");
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
};
//生产线: 下单——-》支付--》交易--》物流
}
5、8锁现象
如何判断锁是谁!永远的知道什么锁,锁到底锁的是谁!
对象,Class
package com.panghl.juc.lock8;
import java.util.concurrent.TimeUnit;
/**
* @Author panghl
* @Date 2021/5/23 19:58
* @Description 8锁,就是关于锁的8个问题
* 1、标准情况下,两个线程先打印 发短信还是打电话?
* 2、sendSms延迟4s,两个线程先打印 发短信 还是打电话
**/
public class Test01 {
public static void main(String[] args) {
Phone phone = new Phone();
//锁的存在
new Thread(() -> {
phone.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.call();
}, "B").start();
}
}
class Phone {
//synchronized 锁的对象是方法的调用者!!
//两个方法用的是同一个锁,谁先拿到谁执行!
public synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call() {
System.out.println("打电话");
}
}
package com.panghl.juc.lock8;
import java.util.concurrent.TimeUnit;
/**
* @Author panghl
* @Date 2021/5/23 19:58
* @Description 8锁,就是关于锁的8个问题
* 3、增加了一个普通方法,这两个线程执行完,是发短信还是hello
* 4、兩個對象,兩個同步方法, 先发短信还是打电话
**/
public class Test02 {
public static void main(String[] args) {
//两个对象
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
//锁的存在
new Thread(() -> {
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone2 {
//synchronized 锁的对象是方法的调用者!!
public synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call() {
System.out.println("打电话");
}
// 这里没有锁!不受锁的影响
public void hello(){
System.out.println("hello");
}
}
package com.panghl.juc.lock8;
import java.util.concurrent.TimeUnit;
/**
* @Author panghl
* @Date 2021/5/23 19:58
* @Description 8锁,就是关于锁的8个问题
* 5、增加两个静态的同步方法,只有一个对象,先发短信还是打电话
* 6、两个对象!增加两个静态的同步方法,先发短信还是打电话
**/
public class Test03 {
public static void main(String[] args) {
//两个对象的Class类模板只有一个,static
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
//锁的存在
new Thread(() -> {
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone3 {
//synchronized 锁的对象是Class
//static 静态方法,类已加载就有了!Class模板
public static synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call() {
System.out.println("打电话");
}
}
package com.panghl.juc.lock8;
import java.util.concurrent.TimeUnit;
/**
* @Author panghl
* @Date 2021/5/23 19:58
* @Description 8锁,就是关于锁的8个问题
* 7、一个静态同步方法,一个非同步方法,只有一个对象,先打印还是发短信 ? 打电话
* 6、两个对象!一个静态同步方法,一个同步方法,先打印先发短信还是打电话
**/
public class Test04 {
public static void main(String[] args) {
//两个对象的Class类模板只有一个,static
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
//锁的存在
new Thread(() -> {
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone4 {
//synchronized 锁的对象是Class
//static 静态方法,类已加载就有了!Class模板
public static synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call() {
System.out.println("打电话");
}
// 这里没有锁!不受锁的影响
public void hello(){
System.out.println("hello");
}
}
小结
new this 具体的一个手机
static Class 唯一的模板
6、集合类不安全
CopyOnWriteArrayList
package com.panghl.juc.unsale;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @Author panghl
* @Date 2021/5/27 13:21
* @Description
**/
//java.util.ConcurrentModificationException 并发修改异常!
public class ListTest {
public static void main(String[] args) {
/*
* 并发下,ArrayList不安全的。
*
* 解决方案:
* 1、List<String> = new Vector<>();
* 2、List<String> list = Collections.synchronizedList(new ArrayList<>());
* 3、List<String> list = new CopyOnWriteArrayList<>();
* CopyOnWrite 谢如师复制 COW 计算机程序设计领域的一种优化策略;
* 多线程调用的时候,list,读取的时候,是固定的。写入(覆盖)
* 在写入的时候避免覆盖,造成数据问题。
* */
List<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();
}
}
}
查看源码可以发现:
CopyOnWriteArraySet
package com.panghl.juc.unsale;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @Author panghl
* @Date 2021/5/27 13:41
* @Description java.util.ConcurrentModificationException 并发修改异常
* 1.Set<Object> set = Collections.synchronizedSet(new HashSet<>());
* 2.CopyOnWriteArraySet<Object> set = new CopyOnWriteArraySet<>();
**/
public class SetList {
public static void main(String[] args) {
// Set<String> set = new HashSet<>();
// Set<Object> set = Collections.synchronizedSet(new HashSet<>());
CopyOnWriteArraySet<Object> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
7、Callable(简单)
1、可以有返回值
2、可以抛出异常
3、方法不同,run()/call()
package com.panghl.juc.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @Author panghl
* @Date 2021/5/27 16:44
**/
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable callable = new MyThread();
FutureTask futureTask = new FutureTask(callable);
//适配类
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();// 结果会被缓存,提高效率
Integer o = (Integer)futureTask.get(); //get方法可能会产生阻塞,把它放在最后
//或者使用异步通信来处理!
System.out.println("返回了--》"+o);
}
}
class MyThread implements Callable{
@Override
public Integer call() throws Exception {
System.out.println("callable is runned");
//耗时的操作
return 1024;
}
}
细节:
1、有缓存
2、结果可能需要等待,会阻塞!
8、常用的辅助类(必会)
8.1、CountDownLatch
package com.panghl.juc.add;
import java.util.concurrent.CountDownLatch;
/**
* @Author panghl
* @Date 2021/5/27 17:32
* @Description 计数器
**/
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 总数是6,必须要执行任务的时候,再使用!
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"gogogo");
countDownLatch.countDown(); //数量-1
},String.valueOf(i)).start(); ;
}
countDownLatch.await(); //等待计数器归零,然后再向下执行
System.out.println("Close Door");
}
}
原理:
countDownLatch.countDown(); //数量-1
countDownLatch.await(); //等待计数器归零,然后再向下执行
每次有线程调用countDown()数量-1,假设计数器变为0, countDownLatch.await()就会被唤醒,继续执行。
8.2、CyclicBarrier
加法计数器
package com.panghl.juc.add;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @Author panghl
* @Date 2021/5/27 17:41
* @Description TODO
**/
public class CyclicBarrierDemo {
public static void main(String[] args) {
/**
* 集齐7颗龙珠召唤神龙
*/
//召唤龙珠的线程
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("召唤神龙成功!");
});
for (int i = 1; i <= 7 ; i++) {
final int temp = i;
//lambda能操作到i吗?
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"收集"+temp+"个龙珠");
try {
cyclicBarrier.await(); //等待
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
8.3、Semaphore
Semaphore:信号量
package com.panghl.juc.add;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* @Author panghl
* @Date 2021/5/27 17:50
* @Description TODO
**/
public class SemaphoreDemo {
public static void main(String[] args) {
//线程数量:停车位
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
//acquire() 得到
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "离开车位");
} catch (Exception e) {
e.printStackTrace();
} finally {
// release() 释放
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
原理:
semaphore.release(); 获得,假设已经满了,等待,等待被释放为止!
semaphore.acquire(); 释放,会将当前的信号量释放+1,然后唤醒等待的线程!
作用:多个共享资源互斥的使用!并发限流,控制最大的线程数!
9.读写锁(ReadWriteLock)
读可以被多线程同时读,写的时候只能有一个线程去写。
package com.panghl.juc.rw;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @Author panghl
* @Date 2021/5/27 18:05
* @Description ReadWriteLock
* 独占锁(写锁)一次只能被一个线程占有
* 共享锁(读锁)多个线程可以同时占有
* ReadWriteLock
* 读-写 可以共存!
* 读-写 不能共存!
* 写-写 不能共存!
**/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCacheLock cache = new MyCacheLock();
//写入
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(() -> {
cache.put(temp + "", temp + "");
}, String.valueOf(i)).start();
}
//读取
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(() -> {
cache.get(temp + "");
}, String.valueOf(i)).start();
}
}
}
/**
* 自定义缓存
*/
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
// 存,写
public void put(String key, Object value) {
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入" + key + "成功");
}
//取,读
public Object get(String key) {
System.out.println(Thread.currentThread().getName() + "读取" + key + "成功");
return map.get(key);
}
}
/**
* 加锁的
*/
class MyCacheLock {
private volatile Map<String, Object> map = new HashMap<>();
//读写锁,更加细粒度的控制
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 存,写 ;写入的时候,只希望同时只有一个线程写
public void put(String key, Object value) {
try {
readWriteLock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入" + key + "成功");
} catch (Exception ex) {
ex.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
//取,读,所有人都可以读!
public Object get(String key) {
try {
readWriteLock.readLock().lock();
System.out.println(Thread.currentThread().getName() + "读取" + key + "成功");
} catch (Exception ex) {
ex.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
return map.get(key);
}
}
由于篇幅原因,请移步JUC02: