1、什么是JUC
源碼 + 官方文檔 面試高頻問(wèn)!
java.util 工具包、包、分類
業(yè)務(wù):普通的線程代碼 Thread Runnable
Runnable 沒(méi)有返回值、效率相比入 Callable 相對(duì)較低!
2、線程和進(jìn)程
線程、進(jìn)程,如果不能使用一句話說(shuō)出來(lái)的技術(shù),不扎實(shí)!
進(jìn)程:一個(gè)程序,QQ.exe Music.exe 程序的集合;
一個(gè)進(jìn)程往往可以包含多個(gè)線程,至少包含一個(gè)!
Java默認(rèn)有幾個(gè)線程? 2 個(gè) mian、GC
線程:開(kāi)了一個(gè)進(jìn)程 Typora,寫字,自動(dòng)保存(線程負(fù)責(zé)的)
對(duì)于Java而言:Thread、Runnable、Callable
Java 真的可以開(kāi)啟線程嗎? 開(kāi)不了
并發(fā)、并行
并發(fā)編程:并發(fā)、并行
并發(fā)(多線程操作同一個(gè)資源)
- CPU 一核 ,模擬出來(lái)多條線程,天下武功,唯快不破,快速交替
并行(多個(gè)人一起行走) - CPU 多核 ,多個(gè)線程可以同時(shí)執(zhí)行; 線程池
并發(fā)編程的本質(zhì):充分利用CPU的資源
線程有幾個(gè)狀態(tài)
public enum State {
// 創(chuàng)建
NEW,
// 運(yùn)行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待,死死地等
WAITING,
// 超時(shí)等待
TIMED_WAITING,
// 終止
TERMINATED;
}
wait/sleep 區(qū)別
1、來(lái)自不同的類
wait => Object
sleep => Thread
2、關(guān)于鎖的釋放
wait 會(huì)釋放鎖,sleep 睡覺(jué)了,抱著鎖睡覺(jué),不會(huì)釋放!
3、使用的范圍是不同的
wait:必須在同步代碼塊中
sleep 可以再任何地方睡
4、是否需要捕獲異常
wait 不需要捕獲異常
sleep 必須要捕獲異常
3、Lock鎖(重點(diǎn))
傳統(tǒng) Synchronized
package com.kuang.demo01;
// 基本的賣票例子
import java.time.OffsetDateTime;
Lock 接口
/**
* 真正的多線程開(kāi)發(fā),公司中的開(kāi)發(fā),降低耦合性
* 線程就是一個(gè)單獨(dú)的資源類,沒(méi)有任何附屬的操作!
* 1、 屬性、方法
*/
public class SaleTicketDemo01 {
public static void main(String[] args) {
// 并發(fā):多線程操作同一個(gè)資源類, 把資源類丟入線程
Ticket ticket = new Ticket();
// @FunctionalInterface 函數(shù)式接口,jdk1.8 lambda表達(dá)式 (參數(shù))->{ 代碼 }
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();
}
}
// 資源類 OOP
class Ticket {
// 屬性、方法
private int number = 30;
// 賣票的方式
// synchronized 本質(zhì): 隊(duì)列,鎖
public synchronized void sale(){
if (number>0){{System.out.println(Thread.currentThread().getName()+"賣出了"+(number--)+"票,剩余:"+number);
}
}
}
Lock 接口
可以在ReentrantLock中設(shè)置公平和非公平鎖
公平鎖:十分公平:可以先來(lái)后到
非公平鎖:十分不公平:可以插隊(duì) (默認(rèn))
Lock三部曲
1. new ReentrantLock();
2. lock.lock(); // 加鎖
3. finally=> lock.unlock(); // 解鎖
測(cè)試效果一樣
Synchronized 和 Lock 區(qū)別
1.Synchronized 內(nèi)置的Java關(guān)鍵字
, Lock 是一個(gè)Java類
2.Synchronized 無(wú)法判斷獲取鎖的狀態(tài)
,Lock 可以判斷是否獲取到了鎖
3.Synchronized 會(huì)自動(dòng)釋放鎖
,lock 必須要手動(dòng)釋放鎖!如果不釋放鎖,死鎖
4.Synchronized 線程 1(獲得鎖,阻塞
)、線程2(等待,傻傻的等
);Lock鎖就不一定會(huì)等待下 去
;
5.Synchronized 可重入鎖
,不可以中斷的
,非公平
;Lock
,可重入鎖
,可以 判斷鎖
,非公平(可以 自己設(shè)置)
;
6.Synchronized 適合鎖少量的代碼同步問(wèn)題
,Lock 適合鎖大量的同步代碼
!
4、生產(chǎn)者和消費(fèi)者問(wèn)題
1)Synchronzied 版本
面試的:?jiǎn)卫J?、排序算法、生產(chǎn)者和消費(fèi)者、死鎖
防止虛假喚醒要用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;
// +1
public synchronized void increment() throws InterruptedException {
// 判斷等待
if (num != 0) {
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName() + "=>" + num);
// 通知其他線程 +1 執(zhí)行完畢
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
// 判斷等待
if (num == 0) {
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "=>" + num);
// 通知其他線程 -1 執(zhí)行完畢
this.notifyAll();
}
}
效果:
2)存在問(wèn)題(虛假喚醒)
問(wèn)題,如果有四個(gè)線程,會(huì)出現(xiàn)虛假喚醒
理解文檔
重點(diǎn)理解if和while的線程喚醒問(wèn)題
解決方式 ,if 改為while即可,防止虛假喚醒
結(jié)論:就是用if判斷的話,喚醒后線程會(huì)從wait之后的代碼開(kāi)始運(yùn)行,但是不會(huì)重新判斷if條件,直接繼續(xù)運(yùn)行if代碼塊之后的代碼,而如果使用while的話,也會(huì)從wait之后的代碼運(yùn)行,但是喚醒后會(huì)重新判斷循環(huán)條件,如果不成立再執(zhí)行while代碼塊之后的代碼塊,成立的話繼續(xù)wait。
這也就是為什么用while而不用if的原因了,因?yàn)榫€程被喚醒后,執(zhí)行開(kāi)始的地方是wait之后
? 拿兩個(gè)加法線程A、B來(lái)說(shuō),比如A先執(zhí)行,執(zhí)行時(shí)調(diào)用了wait方法,那它會(huì)等待,此時(shí)會(huì)釋放鎖,那么線程B獲得鎖并且也會(huì)執(zhí)行wait方法,兩個(gè)加線程一起等待被喚醒。此時(shí)減線程中的某一個(gè)線程執(zhí)行完畢并且喚醒了這倆加線程,那么這倆加線程不會(huì)一起執(zhí)行,其中A獲取了鎖并且加1,執(zhí)行完畢之后B再執(zhí)行。如果是if的話,那么A修改完num后,B不會(huì)再去判斷num的值,直接會(huì)給num+1。如果是while的話,A執(zhí)行完之后,B還會(huì)去判斷num的值,因此就不會(huì)執(zhí)行。
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();
// +1
public void increment() throws InterruptedException {
lock.lock();
try {
// 判斷等待
while (num != 0) {
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName() + "=>" + num);
// 通知其他線程 +1 執(zhí)行完畢
condition.signalAll();
}finally {
lock.unlock();
}
}
// -1
public void decrement() throws InterruptedException {
lock.lock();
try {
// 判斷等待
while (num == 0) {
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName() + "=>" + num);
// 通知其他線程 +1 執(zhí)行完畢
condition.signalAll();
}finally {
lock.unlock();
}
}
}
隨機(jī)執(zhí)行
Condition的優(yōu)勢(shì)
精準(zhǔn)的通知和喚醒的線程!
如何指定線程執(zhí)行的順序? 我們可以使用Condition來(lái)指定通知進(jìn)程~
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 3C
public void printA() {
lock.lock();
try {
// 業(yè)務(wù)代碼 判斷 -> 執(zhí)行 -> 通知
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 {
// 業(yè)務(wù)代碼 判斷 -> 執(zhí)行 -> 通知
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 {
// 業(yè)務(wù)代碼 判斷 -> 執(zhí)行 -> 通知
while (num != 3) {
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "==> CCCC" );
num = 1;
condition1.signal();
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
測(cè)試效果:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-807843.html
JUC并發(fā)編程-線程和進(jìn)程、Synchronized 和 Lock、生產(chǎn)者和消費(fèi)者問(wèn)題 的學(xué)習(xí)筆記到此完結(jié),筆者歸納、創(chuàng)作不易,大佬們給個(gè)3連再起飛吧文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-807843.html
到了這里,關(guān)于JUC并發(fā)編程-線程和進(jìn)程、Synchronized 和 Lock、生產(chǎn)者和消費(fèi)者問(wèn)題的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!