👨🏻🎓博主介绍:大家好,我是芝士味的椒盐,一名在校大学生,热爱分享知识,很高兴在这里认识大家🌟
🌈擅长领域:Java、大数据、运维、电子
🙏🏻如果本文章各位小伙伴们有帮助的话,🍭关注+👍🏻点赞+🗣评论+📦收藏,相应的有空了我也会回访,互助!!!
🤝另本人水平有限,旨在创作简单易懂的文章,在文章描述时如有错,恳请各位大佬指正,在此感谢!!!
@[TOC]
什么是JUC?
- java.util.concurrent在并发编程中使用的工具类
进程/线程是什么?
- 进程:一个程序。例如:QQ。
- 线程:调用进程的资源实现一些功能。例如:在QQ下,我们可以聊天也可以打电话。
- ⚠️ Tips:Java默认有两个线程,一个是main、一个是GC,开线程的三种方法:Thread(普通的线程代码)、Runnable(没有返回值,效率比Callable低)、Callable,Java无法真正的开线程,无法操作硬件,底层使用的是C++,调用本地方法,
并发/并行是什么?
- 并发:多个线程操作一个资源,假的多线程,就是快速交替。
- 并行:多个线程同时执行。
线程几种状态
线程有六种状态
public enum State { //新生 NEW, //运行 RUNNABLE, //阻塞 BLOCKED, //等待,死等 WAITING, //超时等待 TIMED_WAITING, //终止 TERMINATED; }
wait/sleep区别?
- wait→object,sleep→Thread。
- wait会释放锁,sleep不会释放锁。
- wait必须在同步代码块中,sleep任何地方都可以睡。
- wait不用捕获异常,sleep必须要捕获异常
传统锁synchronized
public class ReviewSync {
public static void main(String[] args) {
//资源类
Takits takits = new Takits();
//线程
new Thread(()->{
for (int i = 1; i < 66; i++) {
takits.sale();
}
},"A").start();
new Thread(()->{
for (int i = 1; i < 66; i++) {
takits.sale();
}
},"B").start();
new Thread(()->{
for (int i = 1; i < 66; i++) {
takits.sale();
}
},"C").start();
}
}
class Takits{
private int tick =66;
public synchronized void sale(){
if (tick>0){
System.out.println(Thread.currentThread().getName()+":"+(tick--)+"还剩:"+tick);
}
}
}
Lock锁(重点)
- 先给大家上个锁分类
- lock接口
公平锁:十分公平:可以先来后到
**非公平锁:十分不公平:可以插队 (默认)**
public class ReviewLock {
public static void main(String[] args) {
//资源类
SalveTicks salveTicks = new SalveTicks();
//线程
new Thread(()->{ for (int i = 0; i < 108; i++) {salveTicks.sale();}},"A").start();
new Thread(()->{ for (int i = 0; i < 108; i++) {salveTicks.sale();}},"B").start();
new Thread(()->{ for (int i = 0; i < 108; i++) {salveTicks.sale();}},"C").start();
}
}
class SalveTicks{
private int tick_num=99;
//锁放外卖,里面每次的锁都不一样,无法锁
Lock lock =new ReentrantLock();
public void sale(){
lock.lock();
try {
if (tick_num>0){
System.out.println(Thread.currentThread().getName()+":"+(tick_num--)+"还剩:"+tick_num);
}
}finally {
lock.unlock();
}
}
}
synchronized和Lock区别
1、Synchronized 内置的Java关键字, Lock 是一个Java类
2、Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
3、Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
4、Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下
去;
5、Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以自己设置);
6、Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!
生产者和消费者问题
Synchronized版本
package icu.lookyousmileface.generatorandconsumer; /** * @author starrysky * @title: GeneraAndConsumerSync * @projectName Juc_Pro * @description: sync版本生产者和消费者 * @date 2021/1/299:11 上午 * 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒 * 线程交替执行 A B 操作同一个变量 num = 0 * A num+1 * B num-1 */ public class GeneraAndConsumerSync { public static void main(String[] args) { // 资源类 Data data = new Data(); //线程 new Thread(()->{ for (int i = 0; i < 50; i++) { try { data.inc(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 50; i++) { try { data.dec(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for (int i = 0; i < 50; i++) { try { data.dec(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); new Thread(()->{ for (int i = 0; i < 50; i++) { try { data.dec(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); } } class Data{ private int number = 0; public synchronized void inc() throws InterruptedException { while (number!=0){ this.wait(); } number++; System.out.println(Thread.currentThread().getName()+":"+"number:"+number); //唤醒所有线程 this.notifyAll(); } public synchronized void dec() throws InterruptedException { while (number==0){ //等待唤醒。睡眠 this.wait(); } number--; System.out.println(Thread.currentThread().getName()+":"+"number:"+number); this.notifyAll(); } }
⚠️ Tips:wait应该处于while循环等待中,不应该是if,否则会有虚假唤醒的问题。面试的:单例模式、排序算法、生产者和消费者、死锁
JUC版本(线程非顺序)
- sync和juc版本的替换
package icu.lookyousmileface.generatorandconsumer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author starrysky * @title: GeneraAndConsumerSync * @projectName Juc_Pro * @description: juc版本生产者和消费者 * @date 2021/1/299:11 上午 * 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒 * 线程交替执行 A B 操作同一个变量 num = 0 * A num+1 * B num-1 */ public class GeneraAndConsumerJUC1 { public static void main(String[] args) { // 资源类 Datas data = new Datas(); //线程 new Thread(()->{ for (int i = 0; i < 50; i++) { try { data.inc(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 50; i++) { try { data.dec(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for (int i = 0; i < 50; i++) { try { data.dec(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); new Thread(()->{ for (int i = 0; i < 50; i++) { try { data.dec(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); } } class Datas{ private int number = 0; Lock lock = new ReentrantLock(); //代替了原本的监视器 Condition condition = lock.newCondition(); public void inc() throws InterruptedException { //加锁 lock.lock(); try { while (number!=0){ condition.await(); } number++; System.out.println(Thread.currentThread().getName()+":"+"number:"+number); //通知其他线程 condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void dec() throws InterruptedException { lock.lock(); try { while (number==0){ //等待唤醒。睡眠 condition.await(); } number--; System.out.println(Thread.currentThread().getName()+":"+"number:"+number); condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
⚠️ Tips:这样的JUC是线程无序的
JUC版本(线程顺序)
package icu.lookyousmileface.generatorandconsumer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author starrysky * @title: GeneraAndConsumerSync * @projectName Juc_Pro * @description: juc版本生产者和消费者 * @date 2021/1/299:11 上午 * Condition+ flage标志位实现线程顺序执行 */ public class GeneraAndConsumerJUC2 { public static void main(String[] args) { // 资源类 Data2 data = new Data2(); //线程 new Thread(()->{ for (int i = 0; i < 50; i++) { try { data.A(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 50; i++) { try { data.B(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for (int i = 0; i < 50; i++) { try { data.C(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); } } class Data2{ final Lock lock = new ReentrantLock(); final Condition condition1 = lock.newCondition(); final Condition condition2 = lock.newCondition(); final Condition condition3 = lock.newCondition(); private int flage = 1; public void A() throws InterruptedException { lock.lock(); try { //不是1就睡眠等待 while (flage!=1){ condition1.await(); } flage++; System.out.println(Thread.currentThread().getName()+":"+"A"); //唤醒指定的线程 condition2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void B() throws InterruptedException { lock.lock(); try { while (flage!=2){ condition2.await(); } flage++; System.out.println(Thread.currentThread().getName()+":"+"B"); condition3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void C() throws InterruptedException { lock.lock(); try { while (flage != 3) { condition3.await(); } flage=1; System.out.println(Thread.currentThread().getName() + ":" + "C"); condition1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
⚠️ Tips:Condition优势精准通知和唤醒线程。
8 锁现象
- 锁会锁住:实例对象,Class模版
- 8锁实验代码:
⚠️ Tips:new this 是一个手机,static class是一个手机模版
各种锁的理解
1、公平锁、非公平锁
公平锁: 非常公平, 不能够插队,必须先来后到!
非公平锁:非常不公平,可以插队 (默认都是非公平)
//通过在构造时传入的boolean值选择公平或非公平,默认是非公平
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2、可重入锁
- 可重入锁(递归锁)
- 图解
package icu.lookyousmileface.matchlock;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author starrysky
* @title: RepeatLock
* @projectName Juc_Pro
* @description: 可重复锁,拿到大门的锁相当于拿到卧室的钥匙,lock版本,sync就是作用方法即可
* @date 2021/1/308:56 下午
*/
public class RepeatLock {
public static void main(String[] args) {
Phone phone = new Phone();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
5,
Runtime.getRuntime().availableProcessors(),
5,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
try {
threadPoolExecutor.execute(()->{
phone.sms();
System.out.println(Thread.currentThread().getName()+"=>ok");
});
threadPoolExecutor.execute(()->{
phone.sms();
System.out.println(Thread.currentThread().getName()+"=>ok");
});
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown();
}
}
static class Phone {
ReentrantLock reentrantLock = new ReentrantLock();
public void sms() {
/**
* 上锁和解锁要配对否则会造成死锁
*/
reentrantLock.lock();
try {
System.out.println("Sms");
call();
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public void call() {
reentrantLock.lock();
try {
System.out.println("call");
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
}
}
3、自旋锁
自己创建自旋锁
* @author starrysky * @title: MyLocks * @projectName Juc_Pro * @description: 使用CAS创建的自旋锁 * @date 2021/1/309:16 下午 */ public class MyLocks { private final AtomicReference<Thread> locks = new AtomicReference<>(); public void upLock() { System.out.println(Thread.currentThread().getName()+"=>上锁"); //自旋锁 while (!locks.compareAndSet(null,Thread.currentThread())){ } } public void downLock() { System.out.println(Thread.currentThread().getName()+"=>解锁"); locks.compareAndSet(Thread.currentThread(),null); } }
pool-1-thread-1=>上锁 pool-1-thread-2=>上锁 pool-1-thread-1=>解锁 pool-1-thread-2=>解锁
- 从最开始的上锁开始解锁,前面的锁没有解除,后面的将无法解锁。
4、死锁
试验代码
/** * @author starrysky * @title: DeadLockTest * @projectName Juc_Pro * @description: 死锁测试 * @date 2021/1/309:46 下午 */ public class DeadLockTest { public static void main(String[] args) { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 5, Runtime.getRuntime().availableProcessors(), 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy() ); try { threadPoolExecutor.execute(new MyDeadThread("lockA","lockB")); threadPoolExecutor.execute(new MyDeadThread("lockB","lockA")); } catch (Exception e) { e.printStackTrace(); } finally { threadPoolExecutor.shutdown(); } } }
/** * @author starrysky * @title: MyDeadThread * @projectName Juc_Pro * @description: 线程类 * @date 2021/1/309:51 下午 */ public class MyDeadThread implements Runnable{ private String lockA; private String lockB; public MyDeadThread(String lockA, String lockB) { this.lockA = lockA; this.lockB = lockB; } @Override public void run() { synchronized (lockA){ System.out.println(Thread.currentThread().getName()+"=>lockA"); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB){ System.out.println(Thread.currentThread().getName()+"=>lockB"); } } } }
排除死锁
- 使用jps -l查看正在运行的demo,并记录其PID
- 使用jstack PID可以查看到该程序的堆栈的信息
- ⚠️ Tips:
排查问题:
1、日志 9
2、堆栈 1