1、什么是JUC
源码 + 官方文档 面试高频问!
java.util 工具包、包、分类
业务:普通的线程代码 Thread Runnable
Runnable 没有返回值、效率相比入 Callable 相对较低!
2、线程和进程
线程、进程,如果不能使用一句话说出来的技术,不扎实!
进程:一个程序,QQ.exe Music.exe 程序的集合;
一个进程往往可以包含多个线程,至少包含一个!
Java默认有几个线程? 2 个 mian、GC
线程:开了一个进程 Typora,写字,自动保存(线程负责的)
对于Java而言:Thread、Runnable、Callable
Java 真的可以开启线程吗? 开不了
并发、并行
并发编程:并发、并行
并发(多线程操作同一个资源)
- CPU 一核 ,模拟出来多条线程,天下武功,唯快不破,快速交替
并行(多个人一起行走) - CPU 多核 ,多个线程可以同时执行; 线程池
并发编程的本质:充分利用CPU的资源
线程有几个状态
public enum State {// 创建NEW,// 运行RUNNABLE,// 阻塞BLOCKED,// 等待,死死地等WAITING,// 超时等待TIMED_WAITING,// 终止TERMINATED;
}
wait/sleep 区别
1、来自不同的类
wait => Object
sleep => Thread
2、关于锁的释放
wait 会释放锁,sleep 睡觉了,抱着锁睡觉,不会释放!
3、使用的范围是不同的
wait:必须在同步代码块中
sleep 可以再任何地方睡
4、是否需要捕获异常
wait 不需要捕获异常
sleep 必须要捕获异常
3、Lock锁(重点)
传统 Synchronized
package com.kuang.demo01;
// 基本的卖票例子
import java.time.OffsetDateTime;
Lock 接口
/**
* 真正的多线程开发,公司中的开发,降低耦合性
* 线程就是一个单独的资源类,没有任何附属的操作!
* 1、 属性、方法
*/
public class SaleTicketDemo01 {public static void main(String[] args) {// 并发:多线程操作同一个资源类, 把资源类丢入线程Ticket ticket = new Ticket();// @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }new Thread(()->{for (int i = 1; i < 40 ; i++) {ticket.sale();}},"A").start();new Thread(()->{for (int i = 1; i < 40 ; i++) {ticket.sale();}},"B").start();new Thread(()->{for (int i = 1; i < 40 ; i++) {ticket.sale();}},"C").start();}
}
// 资源类 OOPclass Ticket {// 属性、方法private int number = 30;// 卖票的方式// synchronized 本质: 队列,锁public synchronized void sale(){if (number>0){{System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);}}
}
Lock 接口
可以在ReentrantLock中设置公平和非公平锁
公平锁:十分公平:可以先来后到
非公平锁:十分不公平:可以插队 (默认)
Lock三部曲
1. new ReentrantLock();
2. lock.lock(); // 加锁
3. 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 适合锁大量的同步代码
!
4、生产者和消费者问题
1)Synchronzied 版本
面试的:单例模式、排序算法、生产者和消费者、死锁
防止虚假唤醒要用while,不能用if
package com.marchsoft.juctest;public class ConsumeAndProduct {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 num = 0;// +1public synchronized void increment() throws InterruptedException {// 判断等待if (num != 0) {this.wait();}num++;System.out.println(Thread.currentThread().getName() + "=>" + num);// 通知其他线程 +1 执行完毕this.notifyAll();}// -1public synchronized void decrement() throws InterruptedException {// 判断等待if (num == 0) {this.wait();}num--;System.out.println(Thread.currentThread().getName() + "=>" + num);// 通知其他线程 -1 执行完毕this.notifyAll();}
}
效果:
2)存在问题(虚假唤醒)
问题,如果有四个线程,会出现虚假唤醒
理解文档
重点理解if和while的线程唤醒问题
解决方式 ,if 改为while即可,防止虚假唤醒
结论:就是用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
这也就是为什么用while而不用if的原因了,因为线程被唤醒后,执行开始的地方是wait之后
拿两个加法线程A、B来说,比如A先执行,执行时调用了wait方法,那它会等待,此时会释放锁,那么线程B获得锁并且也会执行wait方法,两个加线程一起等待被唤醒。此时减线程中的某一个线程执行完毕并且唤醒了这俩加线程,那么这俩加线程不会一起执行,其中A获取了锁并且加1,执行完毕之后B再执行。如果是if的话,那么A修改完num后,B不会再去判断num的值,直接会给num+1。如果是while的话,A执行完之后,B还会去判断num的值,因此就不会执行。
3)JUC版
package com.marchsoft.juctest;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockCAP {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 num = 0;Lock lock = new ReentrantLock();Condition condition = lock.newCondition();// +1public void increment() throws InterruptedException {lock.lock();try {// 判断等待while (num != 0) {condition.await();}num++;System.out.println(Thread.currentThread().getName() + "=>" + num);// 通知其他线程 +1 执行完毕condition.signalAll();}finally {lock.unlock();}}// -1public void decrement() throws InterruptedException {lock.lock();try {// 判断等待while (num == 0) {condition.await();}num--;System.out.println(Thread.currentThread().getName() + "=>" + num);// 通知其他线程 +1 执行完毕condition.signalAll();}finally {lock.unlock();}}
}
随机执行
Condition的优势
精准的通知和唤醒的线程!
如何指定线程执行的顺序? 我们可以使用Condition来指定通知进程~
package com.marchsoft.juctest;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ConditionDemo {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 {private Lock lock = new ReentrantLock();private Condition condition1 = lock.newCondition();private Condition condition2 = lock.newCondition();private Condition condition3 = lock.newCondition();private int num = 1; // 1A 2B 3Cpublic void printA() {lock.lock();try {// 业务代码 判断 -> 执行 -> 通知while (num != 1) {condition1.await();}System.out.println(Thread.currentThread().getName() + "==> AAAA" );num = 2;condition2.signal();}catch (Exception e) {e.printStackTrace();}finally {lock.unlock();}}public void printB() {lock.lock();try {// 业务代码 判断 -> 执行 -> 通知while (num != 2) {condition2.await();}System.out.println(Thread.currentThread().getName() + "==> BBBB" );num = 3;condition3.signal();}catch (Exception e) {e.printStackTrace();}finally {lock.unlock();}}public void printC() {lock.lock();try {// 业务代码 判断 -> 执行 -> 通知while (num != 3) {condition3.await();}System.out.println(Thread.currentThread().getName() + "==> CCCC" );num = 1;condition1.signal();}catch (Exception e) {e.printStackTrace();}finally {lock.unlock();}}
}
测试效果:
JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题 的学习笔记到此完结,笔者归纳、创作不易,大佬们给个3连再起飞吧