2019 Java 底層面試題上半場(chǎng)(第一篇)

JUC多線程及高并發(fā)



請(qǐng)談?wù)勀銓?duì)volatile的理解

????volatile是Java虛擬機(jī)提供的輕量級(jí)的同步機(jī)制

? ? 特點(diǎn)是 保證可見(jiàn)性 不保證原子性 禁止指令重排


什么是可見(jiàn)性?

????一個(gè)線程AAA修改了共享變量X的值但還未寫回主內(nèi)存時(shí)蚪燕,另外一個(gè)線程BBB又對(duì)主內(nèi)存中同一個(gè)共享變量X進(jìn)行操作瘫怜,但此時(shí)AAA線程工作內(nèi)存中共享變量X對(duì)線程BBB來(lái)說(shuō)并不可見(jiàn),這種工作內(nèi)存與主內(nèi)存同步延遲現(xiàn)象就造成了可見(jiàn)性問(wèn)題

可見(jiàn)性問(wèn)題演示:

package com.example.demo;

import java.util.concurrent.TimeUnit;

class MyData {

? ? //請(qǐng)注意,此時(shí)number沒(méi)加volatile關(guān)鍵字修飾的向抢,不保證可見(jiàn)性

? ? int number = 0;

? ? public void addTO60() {

? ? ? ? this.number = 60;

? ? }

}

/**

* 可見(jiàn)性問(wèn)題演示

*/

public class VolatileDemo {

? ? public static void main(String[] args) {

? ? ? ? MyData myData = new MyData();//資源類

? ? ? ? new Thread(() -> {

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t come in");

? ? ? ? ? ? //暫停一會(huì)兒線程

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? TimeUnit.SECONDS.sleep(3);

? ? ? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? ? ? myData.addTO60();

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t update number value: " + myData.number);

? ? ? ? }, "AAA").start();

? ? ? ? //第2個(gè)線程就是我們的main線程

? ? ? ? while (myData.number == 0) {

? ? ? ? ? ? //main線程就一直再這里等待循環(huán)套耕,直到number值不再等于0

? ? ? ? }

? ? ? ? System.out.println(Thread.currentThread().getName() + "\t mission is over,main get number value: " + myData.number);

? ? }

}

可見(jiàn)性問(wèn)題解決:

volatile int number =0;


什么是原子性?

不可分割惶凝,也即某個(gè)線程正在做某個(gè)具體業(yè)務(wù)時(shí)吼虎,中間不可以被加塞或者被分割。需要整體完整苍鲜。

要么同時(shí)成功思灰,要么同時(shí)失敗

原子性問(wèn)題演示:

package com.example.demo;

class MyData {

? ? //請(qǐng)注意,此時(shí)number前面是加了volatile關(guān)鍵字修飾的混滔,volatile不保證原子性

? ? volatile int number = 0;

? ? public void addPlusPlus(){

? ? ? ? number++;

? ? }

}

/**

* 原子性問(wèn)題演示

*/

public class VolatileDemo {

? ? public static void main(String[] args) {

? ? ? ? MyData myData = new MyData();//資源類

? ? ? ? for (int i = 1; i <= 20; i++) {

? ? ? ? ? ? new Thread(() ->{

? ? ? ? ? ? ? ? for (int j = 1; j <= 1000; j++) {

? ? ? ? ? ? ? ? ? ? myData.addPlusPlus();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }, String.valueOf(i)).start();

? ? ? ? }

? ? ? ? //需要等待上面20個(gè)線程都全部計(jì)算完成后洒疚,再用main線程取得最終的結(jié)果值看是多少

? ? ? ? while (Thread.activeCount() > 2){

? ? ? ? ? ? Thread.yield();

? ? ? ? }

? ? ? ? System.out.println(Thread.currentThread().getName() + "\t int type, finally number value: "+myData.number);

? ? }

}

原子性問(wèn)題解決:

加sync

加lock

使用JUC下Atomic


有序性(禁止指令)

計(jì)算機(jī)在執(zhí)行程序時(shí),為了提高性能坯屿,編譯器和處理器常常會(huì)對(duì)指令做重排油湖,一般分以下3種

源代碼—>編譯器優(yōu)化的重拍—>指令并行的重拍—>內(nèi)存系統(tǒng)的重排—>最終執(zhí)行的指令

處理器在進(jìn)行重排序時(shí)必須要考慮指令之間的數(shù)據(jù)依賴性

多線程環(huán)境中線程交替執(zhí)行,由于編譯器優(yōu)化重排的存在领跛,兩個(gè)線程中使用的變量能否保證一致性時(shí)無(wú)法確定的乏德,結(jié)果無(wú)法預(yù)測(cè)、

案例:

public void mySort(){

????int x =11;//語(yǔ)句1

? ? int y =q12;//語(yǔ)句2

? ? x = x +5;//語(yǔ)句3

? ? y = x * x;//語(yǔ)句4

}

執(zhí)行順序可能為以下任何一種:1234 ?? 2134 ?? 1324

請(qǐng)問(wèn)語(yǔ)句4可以重排后變成第一條嗎吠昭?

答:不可以喊括,因?yàn)樘幚砥髟谶M(jìn)行重排序時(shí)必須要考慮指令之間的數(shù)據(jù)依賴性


禁止指令重排小總結(jié)

volatile實(shí)現(xiàn)禁止指令重排優(yōu)化,從而避免多線程環(huán)境下程序出現(xiàn)亂序執(zhí)行的現(xiàn)象

原理是通過(guò)插入內(nèi)存屏障禁止在內(nèi)存屏障前后的指令執(zhí)行重排序優(yōu)化


你在哪些地方用到過(guò)volatile矢棚?

多線程下的單例模式DCL(雙端檢鎖):

package com.example.demo;

public class SingletonDemo {

? ? private static volatile SingletonDemo instance = null;

? ? private SingletonDemo(){

? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 我時(shí)構(gòu)造方法SingletonDemo()");

? ? }

? ? //DCL(Doouble Check Lock 雙端檢鎖機(jī)制)

? ? public static SingletonDemo getInstance(){

? ? ? ? if(instance == null){

? ? ? ? ? ? synchronized (SingletonDemo.class){

? ? ? ? ? ? ? ? if(instance == null){

? ? ? ? ? ? ? ? ? ? instance = new SingletonDemo();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return instance;

? ? }

? ? public static void main(String[] args) {

? ? ? ? for (int i = 1; i <= 10; i++) {

? ? ? ? ? ? new Thread(()->{

? ? ? ? ? ? ? ? SingletonDemo.getInstance();

? ? ? ? ? ? }, String.valueOf(i)).start();

? ? ? ? }

? ? }

}


number++在多線程下是非線程安全的郑什,如何不加synchronized解決?

使用我們的JUC下AtomicInteger的getAndIncrement方法


CAS你知道嗎蒲肋?

CAS(CompareAndSwap)比較并交換

比較當(dāng)前工作內(nèi)存中的值和主內(nèi)存中的值蘑拯,如果相同則執(zhí)行規(guī)定操作钝满,否則繼續(xù)向比較直到主內(nèi)存和工作內(nèi)存中的值一致為止

案例:

package com.example.demo;

import java.util.concurrent.atomic.AtomicInteger;

/**

* 比較并交換

*/

public class CASDemo {

? ? public static void main(String[] args) {

? ? ? ? AtomicInteger atomicInteger = new AtomicInteger(5);

????????//main do ting......

? ? ? ? System.out.println(atomicInteger.compareAndSet(5, 2019) + "\t current data: " + atomicInteger.get());

? ? ? ? System.out.println(atomicInteger.compareAndSet(5, 1024) + "\t current data: " + atomicInteger.get());

? ? }

}


CAS底層原理?如果知道申窘,談?wù)勀銓?duì)UnSafe的理解弯蚜?

CAS的操作依賴于Unsafe類的方法,Unsafe類存在于sun.misc包中偶洋,其內(nèi)部方法操作可以像C的指針一樣直接操作內(nèi)存

Unsafe類中的所有方法都是native修飾的熟吏,也就是說(shuō)Unsafe類中的方法都直接調(diào)用操作系統(tǒng)底層資源執(zhí)行相應(yīng)任務(wù)

CAS的全稱為Compare And Swap,它是一條CPU并發(fā)原語(yǔ)玄窝。調(diào)用Unsafe類中的CAS方法牵寺,JVM會(huì)幫我們實(shí)現(xiàn)出CAS匯編指令。這是一種完全依賴于硬件的功能恩脂,通過(guò)它實(shí)現(xiàn)了原子操作帽氓。

由于CAS是一種系統(tǒng)原語(yǔ),原語(yǔ)屬于操作系統(tǒng)用語(yǔ)范圍俩块,是由若干條指令組成的黎休,用于完成某個(gè)功能的一個(gè)過(guò)程,并且原語(yǔ)的執(zhí)行必須是連續(xù)的玉凯,在執(zhí)行過(guò)程中不允許被中斷势腮,也就是說(shuō)CAS是一條CPU的原子指令,不會(huì)造成所謂的數(shù)據(jù)不一致性漫仆。

Unsafe類中CAS(CompareAndSwapInt)捎拯,是一個(gè)本地方法,該方法的實(shí)現(xiàn)位于unsafe.cpp中


CAS缺點(diǎn)盲厌?

循環(huán)時(shí)間長(zhǎng)開(kāi)銷很大(Unsafe類getAndAddInt方法有自旋鎖署照,如果CAS失敗,會(huì)一直進(jìn)行嘗試吗浩。如果CAS長(zhǎng)時(shí)間一直不成功徘溢,會(huì)給CPU帶來(lái)很大的開(kāi)銷)

只能保證一個(gè)共享變量的原子操作(當(dāng)對(duì)一個(gè)共享變量執(zhí)行操作時(shí)领虹,我們可以使用循環(huán)CAS的方式來(lái)保證原子操作 但是 對(duì)多個(gè)共享變量操作時(shí)降允,循環(huán)CAS就無(wú)法保證操作的原子性嘹锁,這個(gè)時(shí)候就可以用鎖來(lái)保證原子性)

引出來(lái)ABA問(wèn)題


原子類AtomicInteger的ABA問(wèn)題談?wù)劊吭痈乱弥绬幔?br>

CAS會(huì)導(dǎo)致“ABA問(wèn)題”

CAS算法實(shí)現(xiàn)一個(gè)重要前提需要取出內(nèi)存中某時(shí)刻的數(shù)據(jù)并在當(dāng)下時(shí)刻比較并替換阀湿,那么在這個(gè)時(shí)間差類會(huì)導(dǎo)致數(shù)據(jù)的變化

比如說(shuō)一個(gè)線程one從內(nèi)存位置V中取出A屡限,這時(shí)候另一個(gè)線程two也從內(nèi)存中取出A,并且線程two進(jìn)行了一些操作將值變成了B炕倘,然后線程two又將V位置的數(shù)據(jù)變成A,這時(shí)候線程one進(jìn)行CAS操作發(fā)現(xiàn)內(nèi)存中仍然是A翰撑,然后線程one操作成功

盡管線程one的CAS操作成功罩旋,但是不代表這個(gè)過(guò)程就是沒(méi)有問(wèn)題的啊央,這個(gè)問(wèn)題可以使用時(shí)間戳原子引用AtomicStampedReference類的getStamp()方法解決。

原子引用(AtomicReference)案例:

package com.example.demo;

import java.util.concurrent.atomic.AtomicReference;

class User{

? ? String userName;

? ? int age;

? ? public User(String userName, int age) {

? ? ? ? this.userName = userName;

? ? ? ? this.age = age;

? ? }

? ? @Override

? ? public String toString() {

? ? ? ? return "User{" +

? ? ? ? ? ? ? ? "userName='" + userName + '\'' +

? ? ? ? ? ? ? ? ", age=" + age +

? ? ? ? ? ? ? ? '}';

? ? }

}

public class AtomicReferenceDemo {

? ? public static void main(String[] args) {

? ? ? ? User z3 = new User("z3", 22);

? ? ? ? User li4 = new User("li4", 25);

? ? ? ? AtomicReference<User> atomicReference = new AtomicReference<>();

? ? ? ? atomicReference.set(z3);

? ? ? ? System.out.println(atomicReference.compareAndSet(z3, li4) + "\t" + atomicReference.get().toString());

? ? ? ? System.out.println(atomicReference.compareAndSet(z3, li4) + "\t" + atomicReference.get().toString());

? ? }

}

時(shí)間戳原子引用(AtomicStampedReference)實(shí)現(xiàn)ABA問(wèn)題和解決ABA問(wèn)題案例:

package com.example.demo;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.atomic.AtomicReference;

import java.util.concurrent.atomic.AtomicStampedReference;

public class ABADemo { //ABA問(wèn)題解決 AtomicStampedReference

? ? static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);

? ? static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

? ? public static void main(String[] args) {

? ? ? ? System.out.println("======================================以下是ABA問(wèn)題的產(chǎn)生=====================================");

? ? ? ? new Thread(()->{

? ? ? ? ? ? atomicReference.compareAndSet(100, 101);

? ? ? ? ? ? atomicReference.compareAndSet(101, 100);

? ? ? ? }, "t1").start();

? ? ? ? new Thread(()->{

? ? ? ? ? ? //暫停1秒鐘t2線程涨醋,保證上面的t1線程完成了一次ABA操作

? ? ? ? ? ? try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }

? ? ? ? ? ? System.out.println(atomicReference.compareAndSet(100, 2019) + "\t" + atomicReference.get());

? ? ? ? }, "t2").start();

? ? ? ? //暫停一會(huì)兒線程

? ? ? ? try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }

? ? ? ? System.out.println("======================================以下是ABA問(wèn)題的解決=====================================");

? ? ? ? new Thread(()->{

? ? ? ? ? ? int stamp = atomicStampedReference.getStamp();

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 第1次版本號(hào):" + stamp);

? ? ? ? ? ? //暫停1秒鐘t3線程

? ? ? ? ? ? try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }

? ? ? ? ? ? atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 第2次版本號(hào):" + atomicStampedReference.getStamp());

? ? ? ? ? ? atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 第3次版本號(hào):" + atomicStampedReference.getStamp());

? ? ? ? }, "t3").start();

? ? ? ? new Thread(()->{

? ? ? ? ? ? int stamp = atomicStampedReference.getStamp();

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 第1次版本號(hào):" + stamp);

? ? ? ? ? ? //暫停3秒鐘t4線程

? ? ? ? ? ? try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }

? ? ? ? ? ? boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 修改成功否:" + result + "\t 當(dāng)前最新實(shí)際版本號(hào):" + atomicStampedReference.getStamp());

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 當(dāng)前實(shí)際最新值:" + atomicStampedReference.getReference());

? ? ? ? }, "t4").start();

? ? }

}


我們知道ArrayList是線程不安全瓜饥,請(qǐng)編碼寫一個(gè)不安全的案例并給出解決方案?

解決ArrayList線程不安全問(wèn)題:

1.使用new Vector<>();

2.使用Collections.synchronizedList();

ArrayList線程不安全問(wèn)題案例:

package com.example.demo;

import java.util.*;

/**

* 集合類不安全的問(wèn)題

* ArrayList

*/

public class ContainerNotSafeDemo {

? ? public static void main(String[] args) {

? ? ? ? List<String> list = new ArrayList<>();

? ? ? ? for (int i = 1; i <= 30; i++) {

? ? ? ? ? ? new Thread(()->{

? ? ? ? ? ? ? ? list.add(UUID.randomUUID().toString().substring(0,8));

? ? ? ? ? ? ? ? System.out.println(list);

? ? ? ? ? ? }, String.valueOf(i)).start();

? ? ? ? }

? ? ? ? /**

? ? ? ? * 1 故障現(xiàn)象

? ? ? ? * java.util.ConcurrentModificationException

? ? ? ? *

? ? ? ? * 2 導(dǎo)致原因

????????* 并發(fā)爭(zhēng)搶修改導(dǎo)致浴骂,參考我們的花名冊(cè)簽名情況乓土。?

????????* 一個(gè)人正在寫入,另外一個(gè)同學(xué)過(guò)來(lái)?yè)寠Z溯警,導(dǎo)致數(shù)據(jù)不一致異常趣苏,并發(fā)修改異常。

? ? ? ? *

? ? ? ? * 3 解決方案

? ? ? ? * new Vector<>();

? ? ? ? * Collections.synchronizedList(new ArrayList<>());

? ? ? ? */

? ? }

}

不用Vector和Collections工具類解決:

解決集合類不安全之List

new CopyOnWriteArrayList();

解決集合類不安全之Set

new CopyOnWriteArraySet();

解決集合類不安全之Map

new ConcurrentHashMap();


公平鎖和非公平鎖梯轻?

公平鎖是指多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖食磕,類似排隊(duì)打飯,先來(lái)后到喳挑。

非公平鎖是指多個(gè)線程獲取鎖的順序并不是按照申請(qǐng)鎖的順序彬伦,有可能后申請(qǐng)的線程比先申請(qǐng)的線程優(yōu)先獲取鎖。在高并發(fā)的情況下伊诵,有可能會(huì)造成優(yōu)先級(jí)反轉(zhuǎn)或者饑餓現(xiàn)象

關(guān)于兩者區(qū)別:

公平鎖单绑,在并發(fā)環(huán)境中,每個(gè)線程在獲取鎖時(shí)會(huì)先查看此鎖維護(hù)的等待隊(duì)列曹宴,如果為空搂橙,或者當(dāng)前線程是等待隊(duì)列的第一個(gè),就占有鎖浙炼,否則就會(huì)加入到等待隊(duì)列中份氧,以后會(huì)按照FIFO的規(guī)則從隊(duì)列中取到自己

非公平鎖比較粗魯,上來(lái)就直接嘗試占有鎖弯屈,如果嘗試失敗蜗帜,就再采用類似公平鎖那種方式

Java ReentrantLock而言,通過(guò)構(gòu)造函數(shù)指定該鎖是否是公平鎖资厉,默認(rèn)是非公平鎖厅缺。非公平鎖的優(yōu)點(diǎn)在于吞吐量比公平鎖大對(duì)于Synchronized而言,也是一種非公平鎖


可重入鎖宴偿?

可重入鎖(又名遞歸鎖)在同一個(gè)線程再外層方法獲取鎖的時(shí)候湘捎,再進(jìn)入內(nèi)層方法會(huì)自動(dòng)獲取鎖

ReentrantLock/Synchronized就是一個(gè)典型的可重入鎖

可重入鎖最大的作用是避免死鎖

Synchronized可重入鎖案例:

package com.example.demo;

class Phone{

? ? public synchronized void sendSMS() throws Exception{

? ? ? ? System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS()");

? ? ? ? sendEmail();

? ? }

? ? public synchronized void sendEmail() throws Exception{

? ? ? ? System.out.println(Thread.currentThread().getName() + "\t invoked sendEmail()");

? ? }

}

public class ReentrantLockDemo {

? ? public static void main(String[] args) {

? ? ? ? Phone phone = new Phone();

? ? ? ? new Thread(()->{

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? phone.sendSMS();

? ? ? ? ? ? }catch (Exception e){

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }, "t1").start();

? ? ? ? new Thread(()->{

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? phone.sendSMS();

? ? ? ? ? ? }catch (Exception e){

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }, "t2").start();

? ? }

}

ReentrantLock可重入鎖案例:

package com.example.demo;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

class Phone implements Runnable{

? ? Lock lock = new ReentrantLock();

? ? @Override

? ? public void run() {

? ? ? ? get();

? ? }

? ? public void get() {

? ? ? ? lock.lock();

? ? ? ? try {

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t invoked get()");

? ? ? ? ? ? set();

? ? ? ? }finally {

? ? ? ? ? ? lock.unlock();

? ? ? ? }

? ? }

? ? public void set() {

? ? ? ? lock.lock();

? ? ? ? try {

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t invoked set()");

? ? ? ? }finally {

? ? ? ? ? ? lock.unlock();

? ? ? ? }

? ? }

}

public class ReentrantLockDemo {

? ? public static void main(String[] args) {

? ? ? ? Phone phone = new Phone();

? ? ? ? Thread t3 = new Thread(phone,"t3");

? ? ? ? Thread t4 = new Thread(phone,"t4");

? ? ? ? t3.start();

? ? ? ? t4.start();

? ? }

}


自旋鎖?

是指嘗試獲取鎖的線程不會(huì)立即阻塞窄刘,而是采用循環(huán)的方式嘗試獲取鎖,這樣的好處是減少線程上下文切換的消耗窥妇,缺點(diǎn)是循環(huán)會(huì)消耗CPU。

自選鎖案例:

package com.example.demo;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.atomic.AtomicReference;

public class SpinLockDemo {

? ? //原子引用線程娩践,引用類型初始值null

? ? AtomicReference<Thread> atomicReference = new AtomicReference<>();

? ? public static void main(String[] args) {

? ? ? ? SpinLockDemo spinLockDemo = new SpinLockDemo();

? ? ? ? new Thread(() -> {

? ? ? ? ? ? spinLockDemo.myLock();

? ? ? ? ? ? //暫停一會(huì)兒線程

? ? ? ? ? ? try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }

? ? ? ? ? ? spinLockDemo.myUnlock();

? ? ? ? }, "AA").start();

? ? ? ? try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }

? ? ? ? new Thread(() -> {

? ? ? ? ? ? spinLockDemo.myLock();

? ? ? ? ? ? spinLockDemo.myUnlock();

? ? ? ? }, "BB").start();

? ? }

? ? public void myUnlock() {

? ? ? ? Thread thread = Thread.currentThread();

? ? ? ? atomicReference.compareAndSet(thread, null);

? ? ? ? System.out.println(Thread.currentThread().getName() + "\t invoked myUnlock()");

? ? }

? ? public void myLock() {

? ? ? ? Thread thread = Thread.currentThread();

? ? ? ? System.out.println(Thread.currentThread().getName() + "\t come in O(∩_∩)O");

? ? ? ? while (!atomicReference.compareAndSet(null, thread)) {

? ? ? ? }

? ? }

}


獨(dú)占鎖(寫)活翩,共享鎖(讀)烹骨,互斥鎖?

獨(dú)占鎖:指該鎖一次只能被一個(gè)線程所持有材泄。對(duì)ReentrantLock和Synchronized而言都是獨(dú)占鎖

共享鎖:指該鎖可被多個(gè)線程所持有沮焕。對(duì)ReentrantReadWriteLock其讀鎖是共享鎖,其寫鎖是獨(dú)占鎖

互斥鎖:在一個(gè)線程修改變量時(shí)加鎖拉宗,則其他變量阻塞峦树,等待加鎖的變量解鎖后再執(zhí)行

主要使用JUC下的ReentrantReadWriteLock讀寫鎖類的writeLock() readLock()方法實(shí)現(xiàn)

以上三個(gè)鎖也可合稱為讀寫鎖

讀寫鎖讀寫沖突問(wèn)題案例:

package com.example.demo;

import java.util.HashMap;

import java.util.Map;

import java.util.concurrent.TimeUnit;

class MyCache {

? ? private volatile Map<String, Object> map = new HashMap();

? ? public void put(String key, Object value) {

? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 正在寫入:" + key);

? ? ? ? //暫停一會(huì)兒線程 模擬網(wǎng)絡(luò)擁堵

? ? ? ? try {

? ? ? ? ? ? TimeUnit.MILLISECONDS.sleep(300);

? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? ? ? map.put(key, value);

? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 寫入完成");

? ? }

? ? public void get(String key) {

? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 正在讀取:" + key);

? ? ? ? //暫停一會(huì)兒線程 模擬網(wǎng)絡(luò)擁堵

? ? ? ? try {

? ? ? ? ? ? TimeUnit.MILLISECONDS.sleep(300);

? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? ? ? Object result = map.get(key);

? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 讀取完成:" + result);

? ? }

}

public class ReadWriteLockDemo {

? ? public static void main(String[] args) {

? ? ? ? MyCache myCache = new MyCache();

? ? ? ? //5個(gè)線程寫

? ? ? ? for (int i = 1; i <= 5; i++) {

? ? ? ? ? ? final int tempInt = i;

? ? ? ? ? ? new Thread(() -> {

? ? ? ? ? ? ? ? myCache.put(tempInt + "", tempInt + "");

? ? ? ? ? ? }, String.valueOf(i)).start();

? ? ? ? }

? ? ? ? //5個(gè)線程讀

? ? ? ? for (int i = 1; i <= 5; i++) {

? ? ? ? ? ? final int tempInt = i;

? ? ? ? ? ? new Thread(() -> {

? ? ? ? ? ? ? ? myCache.get(tempInt + "");

? ? ? ? ? ? }, String.valueOf(i)).start();

? ? ? ? }

? ? }

}

加入ReentrantReadWriteLock解決讀寫沖突問(wèn)題:

package com.example.demo;

import java.util.HashMap;

import java.util.Map;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.ReentrantReadWriteLock;

class MyCache {

? ? private volatile Map<String, Object> map = new HashMap();

? ? private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

? ? public void put(String key, Object value) {

? ? ? ? rwLock.writeLock().lock();

? ? ? ? try {

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 正在寫入:" + key);

? ? ? ? ? ? //暫停一會(huì)兒線程 模擬網(wǎng)絡(luò)擁堵

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? TimeUnit.MILLISECONDS.sleep(300);

? ? ? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? ? ? map.put(key, value);

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 寫入完成");

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? } finally {

? ? ? ? ? ? rwLock.writeLock().unlock();

? ? ? ? }

? ? }

? ? public void get(String key) {

? ? ? ? rwLock.readLock().lock();

? ? ? ? try {

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 正在讀鹊┦隆:" + key);

? ? ? ? ? ? //暫停一會(huì)兒線程 模擬網(wǎng)絡(luò)擁堵

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? TimeUnit.MILLISECONDS.sleep(300);

? ? ? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? ? ? Object result = map.get(key);

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 讀取完成:" + result);

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? } finally {

? ? ? ? ? ? rwLock.readLock().unlock();

? ? ? ? }

? ? }

}

public class ReadWriteLockDemo {

? ? public static void main(String[] args) {

? ? ? ? MyCache myCache = new MyCache();

? ? ? ? //5個(gè)線程寫

? ? ? ? for (int i = 1; i <= 5; i++) {

? ? ? ? ? ? final int tempInt = i;

? ? ? ? ? ? new Thread(() -> {

? ? ? ? ? ? ? ? myCache.put(tempInt + "", tempInt + "");

? ? ? ? ? ? }, String.valueOf(i)).start();

? ? ? ? }

? ? ? ? //5個(gè)線程讀

? ? ? ? for (int i = 1; i <= 5; i++) {

? ? ? ? ? ? final int tempInt = i;

? ? ? ? ? ? new Thread(() -> {

? ? ? ? ? ? ? ? myCache.get(tempInt + "");

? ? ? ? ? ? }, String.valueOf(i)).start();

? ? ? ? }

? ? }

}


CountDownLatch魁巩?

讓一些線程阻塞直到另一些線程完成一系列操作后才被喚醒

CountDownLatch主要有兩個(gè)方法,當(dāng)一個(gè)或多個(gè)線程調(diào)用await方法時(shí)族檬,調(diào)用線程會(huì)被阻塞歪赢。

其他線程調(diào)用countDown方法會(huì)將計(jì)數(shù)器減1(調(diào)用countDown方法的線程不會(huì)阻塞)。

當(dāng)計(jì)數(shù)器的值變?yōu)?時(shí)单料,因調(diào)用await方法被阻塞的線程會(huì)被喚醒埋凯,繼續(xù)執(zhí)行。

案例:

CountDownLatchDemo.java

package com.example.demo;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {

? ? public static void main(String[] args) throws Exception {

? ? ? ? CountDownLatch countDownLatch = new CountDownLatch(6);

? ? ? ? for (int i = 1; i <= 6; i++) {

? ? ? ? ? ? new Thread(()->{

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 國(guó)扫尖,被滅");

? ? ? ? ? ? ? ? countDownLatch.countDown();

? ? ? ? ? ? }, CountryEnum.forEach_countryEnum(i).getRetMessage()).start();

? ? ? ? }

? ? ? ? countDownLatch.await();

? ? ? ? System.out.println(Thread.currentThread().getName() + "\t **********秦國(guó)一統(tǒng)華夏");

? ? }

}


CountryEnum.java

package com.example.demo;

public enum CountryEnum {

? ? ONE(1, "齊"),

? ? TWO(2, "楚"),

? ? THREE(3, "燕"),

? ? FOUR(4, "趙"),

? ? FIVE(5, "魏"),

? ? SIX(6, "韓");

? ? private Integer retCode;

? ? private String retMessage;

? ? public Integer getRetCode() {

? ? ? ? return retCode;

? ? }

? ? public String getRetMessage() {

? ? ? ? return retMessage;

? ? }

? ? CountryEnum(Integer retCode, String retMessage) {

? ? ? ? this.retCode = retCode;

? ? ? ? this.retMessage = retMessage;

? ? }

? ? public static CountryEnum forEach_countryEnum(int index) {

? ? ? ? CountryEnum[] myArray = CountryEnum.values();

? ? ? ? for (CountryEnum element : myArray) {

? ? ? ? ? ? if (index == element.getRetCode()) {

? ? ? ? ? ? ? ? return element;

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return null;

? ? }

}


CyclicBarrier白对?

CyclicBarrier的字面意思是可循環(huán)(Cyclic)使用的屏障(Barrier)。它要做的事情是换怖,

讓一組線程到達(dá)一個(gè)屏障(也可以叫同步點(diǎn))時(shí)被阻塞甩恼,直到最后一個(gè)線程到達(dá)屏障時(shí),

屏障才會(huì)開(kāi)門沉颂,所有被屏障攔截的線程才會(huì)繼續(xù)干活条摸,線程進(jìn)入屏障通過(guò)CyclicBarrier的await()方法。

案例:

package com.example.demo;

import java.util.concurrent.BrokenBarrierException;

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {

? ? public static void main(String[] args) {

? ? ? ? CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {

? ? ? ? ? ? System.out.println("********召喚神龍");

? ? ? ? });

? ? ? ? for (int i = 1; i <= 7; i++) {

? ? ? ? ? ? final int tempInt = i;

? ? ? ? ? ? new Thread(() -> {

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 收集到第:" + tempInt + "顆龍珠");

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? cyclicBarrier.await();

? ? ? ? ? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? ? ? } catch (BrokenBarrierException e) {

? ? ? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }, String.valueOf(i)).start();

? ? ? ? }

? ? }

}


Semaphore铸屉?

主要用于兩個(gè)目的钉蒲,一個(gè)是用于多個(gè)共享資源的互斥使用,另一個(gè)用于并發(fā)線程數(shù)的控制彻坛。

案例:

package com.example.demo;

import java.util.concurrent.Semaphore;

import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {

? ? public static void main(String[] args) {

? ? ? ? Semaphore semaphore = new Semaphore(3);//模擬3個(gè)停車位

? ? ? ? for (int i = 1; i <= 6; i++) {//模擬6部汽車

? ? ? ? ? ? new Thread(() -> {

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? semaphore.acquire();

? ? ? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"\t 搶到車位");

? ? ? ? ? ? ? ? ? ? try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }

? ? ? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+ "\t 停車3秒后離開(kāi)車位");

? ? ? ? ? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? ? ? }finally {

? ? ? ? ? ? ? ? ? ? semaphore.release();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }, String.valueOf(i)).start();

? ? ? ? }

? ? }

}


阻塞隊(duì)列顷啼?

當(dāng)阻塞隊(duì)列是空時(shí),從隊(duì)列中獲取元素的操作將會(huì)被阻塞昌屉。

當(dāng)阻塞隊(duì)列是滿時(shí)钙蒙,往隊(duì)列中添加元素的操作將會(huì)被阻塞。

在多線程領(lǐng)域:所謂阻塞间驮,在某些情況下會(huì)掛起線程(即阻塞)躬厌,一旦條件滿足,被掛起的線程又會(huì)自動(dòng)被喚醒

阻塞隊(duì)列 (BlockingQueue)



為什么需要BlockingQueue竞帽?

好處是我們不需要關(guān)心什么時(shí)候需要阻塞線程烤咧,什么時(shí)候需要喚醒線程偏陪,因?yàn)檫@一切BlockingQueue都給你一手包辦了

在conciurrent發(fā)布以前,在多線程環(huán)境下煮嫌,我們每個(gè)程序員都必須去自己控制這些細(xì)節(jié),尤其還要兼顧效率和線程安全抱虐,而這會(huì)給我們的重新帶來(lái)不小的復(fù)雜度昌阿。

BlockingQueue的核心方法?

拋出異常:

1)當(dāng)阻塞隊(duì)列滿時(shí)恳邀,再往隊(duì)列里add插入元素會(huì)拋出異常IIIegalStateException: Queue full

2)當(dāng)阻塞隊(duì)列空時(shí)懦冰,再往隊(duì)列里remove移除元素會(huì)拋異常NoSuchElementException

3)當(dāng)隊(duì)列為空時(shí), element會(huì)拋出一個(gè)異常

特殊值:

1)offer插入方法谣沸,成功true失敗false

2)poll移除方法刷钢,成功返回出隊(duì)列的元素,隊(duì)列里沒(méi)有就返回null

3)當(dāng)隊(duì)列為空時(shí)乳附,peek返回null

一直阻塞:

1)當(dāng)阻塞隊(duì)列滿時(shí)内地,生產(chǎn)者線程繼續(xù)往隊(duì)列里put元素,隊(duì)列會(huì)一直阻塞生產(chǎn)線程直到put數(shù)據(jù)or響應(yīng)中斷退出

2)當(dāng)阻塞隊(duì)列空時(shí)赋除,消費(fèi)者線程試圖從隊(duì)列里take元素阱缓,隊(duì)列會(huì)一直阻塞消費(fèi)者線程直到隊(duì)列可用

超時(shí)退出:

1)當(dāng)阻塞隊(duì)列滿時(shí),隊(duì)列會(huì)阻塞生產(chǎn)者線程一定時(shí)間举农,超過(guò)限時(shí)后生產(chǎn)者線程會(huì)退出


BlockingQueue架構(gòu)荆针?

Collection

->

Queue

->

BlockingQueue

->

LinkedTransferQueue:由鏈表結(jié)構(gòu)組成的無(wú)界阻塞隊(duì)列。

LinkedBlockingDeque:由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列颁糟。

PriorityBlockingQueue:支持優(yōu)先級(jí)排序的無(wú)界阻塞隊(duì)列航背。

SynchronousQueue:不存儲(chǔ)元素的阻塞隊(duì)列,也即單個(gè)元素的隊(duì)列棱貌。

DelayQueue:使用優(yōu)先級(jí)隊(duì)列實(shí)現(xiàn)的延遲無(wú)界阻塞隊(duì)列玖媚。

ArrayBlockingQueue:由數(shù)組結(jié)構(gòu)組成的有界阻塞隊(duì)列。

LinkedBlockingQueue:由鏈表結(jié)構(gòu)組成的有界(但大小默認(rèn)值位Integer.MAX_VALUE)阻塞隊(duì)列


ArrayBlockingQueue / LinkedBlockingQueue使用键畴?

BlockingQueue<String> blockingQueue = new ArrayBlockingQueue();

BlockingQueue<String> blockingQueue = new LinkedBlockingQueue();

然后參考 BlockingQueue的核心方法 來(lái)使用


SynchronousQueue最盅?

與其他BlockingQueue不同,SynchronousQueue是一個(gè)不存儲(chǔ)元素的BlockingQueue

每一個(gè)put操作必須要等待一個(gè)take操作起惕,否則不能繼續(xù)添加元素涡贱,反之亦然。

案例:

package com.example.demo;

import java.util.concurrent.BlockingQueue;

import java.util.concurrent.SynchronousQueue;

import java.util.concurrent.TimeUnit;

public class SynchronousQueueDemo {

? ? public static void main(String[] args) {

? ? ? ? BlockingQueue<String> blockingQueue = new SynchronousQueue();

? ? ? ? new Thread(()->{

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"\t put 1");

? ? ? ? ? ? ? ? blockingQueue.put("1");

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"\t put 2");

? ? ? ? ? ? ? ? blockingQueue.put("2");

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"\t put 3");

? ? ? ? ? ? ? ? blockingQueue.put("3");

? ? ? ? ? ? }catch (Exception e){

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? },"AAA").start();

? ? ? ? new Thread(()->{

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());

? ? ? ? ? ? ? ? try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());

? ? ? ? ? ? ? ? try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());

? ? ? ? ? ? }catch (Exception e){

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? },"BBB").start();

? ? }

}


JUC1.0和2.0惹想?

synchronized被lock替代

wait被await替代

notify被sinqal替代


阻塞隊(duì)列用在哪问词?

生產(chǎn)者消費(fèi)者模式

線程池

消息中間件


傳統(tǒng)版阻塞隊(duì)列(生產(chǎn)者消費(fèi)者)實(shí)現(xiàn)?

題目:一個(gè)初始值為零的變量嘀粱,兩個(gè)線程對(duì)其交替操作激挪,一個(gè)加1一個(gè)減1辰狡,來(lái)5輪 案例:

package com.example.demo;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

class ShareData {

? ? private int number = 0;

? ? private Lock lock = new ReentrantLock();

? ? private Condition condition = lock.newCondition();

? ? public void increment() throws Exception {

? ? ? ? lock.lock();

? ? ? ? try {

? ? ? ? ? ? //1 判斷

? ? ? ? ? ? while (number != 0) {

? ? ? ? ? ? ? ? //等待,不能生產(chǎn)

? ? ? ? ? ? ? ? condition.await();

? ? ? ? ? ? }

? ? ? ? ? ? //2 干活

? ? ? ? ? ? number++;

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t" + number);

? ? ? ? ? ? //3 通知喚醒

? ? ? ? ? ? condition.signalAll();

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? } finally {

? ? ? ? ? ? lock.unlock();

? ? ? ? }

? ? }

? ? public void decrement() throws Exception {

? ? ? ? lock.lock();

? ? ? ? try {

? ? ? ? ? ? //1 判斷

? ? ? ? ? ? while (number == 0) {

? ? ? ? ? ? ? ? //等待垄分,不能生產(chǎn)

? ? ? ? ? ? ? ? condition.await();

? ? ? ? ? ? }

? ? ? ? ? ? //2 干活

? ? ? ? ? ? number--;

? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t" + number);

? ? ? ? ? ? //3 通知喚醒

? ? ? ? ? ? condition.signalAll();

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? } finally {

? ? ? ? ? ? lock.unlock();

? ? ? ? }

? ? }

}

/**

* 題目:一個(gè)初始值為零的變量宛篇,兩個(gè)線程對(duì)其交替操作,一個(gè)加1一個(gè)減1薄湿,來(lái)5輪

*/

public class ProdConsumer_TraditionDemo {

? ? public static void main(String[] args) {

? ? ? ? ShareData shareData = new ShareData();

? ? ? ? new Thread(() -> {

? ? ? ? ? ? for (int i = 1; i <= 5; i++) {

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? shareData.increment();

? ? ? ? ? ? ? ? } catch (Exception e) {

? ? ? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }, "AA").start();

? ? ? ? new Thread(() -> {

? ? ? ? ? ? for (int i = 1; i <= 5; i++) {

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? shareData.decrement();

? ? ? ? ? ? ? ? } catch (Exception e) {

? ? ? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }, "BB").start();

? ? }

}


JUC3.0版?zhèn)鹘y(tǒng)版阻塞隊(duì)列(生產(chǎn)者消費(fèi)者)實(shí)現(xiàn)叫倍?

package com.example.demo;

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.BlockingQueue;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.atomic.AtomicInteger;

class MyResource {

? ? private volatile boolean FLAG = true;? //默認(rèn)開(kāi)啟,進(jìn)行生產(chǎn)+消費(fèi)

? ? private AtomicInteger atomicInteger = new AtomicInteger();

? ? BlockingQueue<String> blockingQueue = null;

? ? public MyResource(BlockingQueue blockingQueue) {

? ? ? ? this.blockingQueue = blockingQueue;

? ? ? ? System.out.println(blockingQueue.getClass().getName());

? ? }

? ? public void MyProd() throws Exception {

? ? ? ? String data = null;

? ? ? ? boolean retValue;

? ? ? ? while (FLAG) {

? ? ? ? ? ? data = atomicInteger.incrementAndGet() + "";

? ? ? ? ? ? retValue = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);

? ? ? ? ? ? if (retValue) {

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 插入隊(duì)列" + data + "成功");

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 插入隊(duì)列" + data + "失敗");

? ? ? ? ? ? }

? ? ? ? ? ? TimeUnit.SECONDS.sleep(1);

? ? ? ? }

? ? ? ? System.out.println(Thread.currentThread().getName() + "\t 大老板叫停了豺瘤,表示FLAG=false吆倦,生產(chǎn)動(dòng)作結(jié)束");

? ? }

? ? public void MyConsumer() throws Exception {

? ? ? ? String result = null;

? ? ? ? while (FLAG) {

? ? ? ? ? ? result = blockingQueue.poll(2L, TimeUnit.SECONDS);

? ? ? ? ? ? if (null == result || result.equalsIgnoreCase("")) {

? ? ? ? ? ? ? ? FLAG = false;

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"\t 超過(guò)2秒鐘沒(méi)有取到蛋糕,消費(fèi)退出");

? ? ? ? ? ? ? ? System.out.println();

? ? ? ? ? ? ? ? System.out.println();

? ? ? ? ? ? ? ? System.out.println();

? ? ? ? ? ? ? ? return;

? ? ? ? ? ? }

? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"\t 消費(fèi)隊(duì)列蛋糕"+result+"成功");

? ? ? ? }

? ? }

? ? public void stop() throws Exception{

? ? ? ? this.FLAG = false;

? ? }

}

public class ProdConsumer_BlockQueueDemo {

? ? public static void main(String[] args) throws Exception {

? ? ? ? MyResource myResource = new MyResource(new ArrayBlockingQueue(10));

? ? ? ? new Thread(()->{

? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"\t 生產(chǎn)線程啟動(dòng)");

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? myResource.MyProd();

? ? ? ? ? ? } catch (Exception e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? },"Prod").start();

? ? ? ? new Thread(()->{

? ? ? ? ? ? System.out.println(Thread.currentThread().getName()+"\t 消費(fèi)線程啟動(dòng)");

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? myResource.MyConsumer();

? ? ? ? ? ? } catch (Exception e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? },"Consumer").start();

? ? ? ? try {TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }

? ? ? ? System.out.println();

? ? ? ? System.out.println();

? ? ? ? System.out.println();

? ? ? ? System.out.println("5秒鐘時(shí)間到坐求,大老板main線程叫停蚕泽,活動(dòng)結(jié)束");

? ? ? ? myResource.stop();

? ? }

}


Synchronized和Lock有什么區(qū)別?用新的Lock有什么好處桥嗤?舉例說(shuō)明

1)synchronized是關(guān)鍵字须妻。Lock是具體類。

2)synchronized不需要用戶手動(dòng)去釋放鎖砸逊。 ReentrantLock需要用戶去手動(dòng)釋放鎖 lock() 和 unlock() 方法璧南。

3)synchronized執(zhí)行過(guò)程中不可中斷,除非拋出異呈σ荩或運(yùn)行完成司倚。?ReentrantLock可中斷 方法一:設(shè)置超時(shí)方法 tryLock() 方法二:lockInterruptibly() 放到代碼塊中調(diào)用 interrupt() 方法可中斷

4)synchronized是非公平鎖。ReentrantLock默認(rèn)非公平鎖 ReentrantLock(true)后可以改為公平鎖

5)synchronized沒(méi)有Condition篓像。?ReentrantLock可以使用Condition來(lái)實(shí)現(xiàn)精確喚醒線程动知,而不像synchronized要么隨機(jī)喚醒一個(gè)線程要么喚醒全部線程

題目:多線程之間按順序調(diào)用,實(shí)現(xiàn)A->B->C三個(gè)線程啟動(dòng)员辩,要求如下:

AA打印5次盒粮,BB打印10次,CC打印15次

緊接著

AA打印5次奠滑,BB打印10次丹皱,CC打印15次

......

來(lái)10輪

package com.example.demo;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

class ShareResource {

? ? private int number = 1; //A:1 B:2 C:3

? ? private Lock lock = new ReentrantLock();

? ? private Condition c1 = lock.newCondition();

? ? private Condition c2 = lock.newCondition();

? ? private Condition c3 = lock.newCondition();

? ? public void print5() {

? ? ? ? lock.lock();

? ? ? ? try {

? ? ? ? ? ? //1 判斷

? ? ? ? ? ? while (number != 1) {

? ? ? ? ? ? ? ? c1.await();

? ? ? ? ? ? }

? ? ? ? ? ? //2 干活

? ? ? ? ? ? for (int i = 1; i <= 5; i++) {

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t" + i);

? ? ? ? ? ? }

? ? ? ? ? ? //3 通知

? ? ? ? ? ? number = 2;

? ? ? ? ? ? c2.signal();

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? } finally {

? ? ? ? ? ? lock.unlock();

? ? ? ? }

? ? }

? ? public void print10() {

? ? ? ? lock.lock();

? ? ? ? try {

? ? ? ? ? ? //1 判斷

? ? ? ? ? ? while (number != 2) {

? ? ? ? ? ? ? ? c2.await();

? ? ? ? ? ? }

? ? ? ? ? ? //2 干活

? ? ? ? ? ? for (int i = 1; i <= 10; i++) {

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t" + i);

? ? ? ? ? ? }

? ? ? ? ? ? //3 通知

? ? ? ? ? ? number = 3;

? ? ? ? ? ? c3.signal();

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? } finally {

? ? ? ? ? ? lock.unlock();

? ? ? ? }

? ? }

? ? public void print15() {

? ? ? ? lock.lock();

? ? ? ? try {

? ? ? ? ? ? //1 判斷

? ? ? ? ? ? while (number != 3) {

? ? ? ? ? ? ? ? c3.await();

? ? ? ? ? ? }

? ? ? ? ? ? //2 干活

? ? ? ? ? ? for (int i = 1; i <= 15; i++) {

? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "\t" + i);

? ? ? ? ? ? }

? ? ? ? ? ? //3 通知

? ? ? ? ? ? number = 1;

? ? ? ? ? ? c1.signal();

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? } finally {

? ? ? ? ? ? lock.unlock();

? ? ? ? }

? ? }

}

/**

* 題目:多線程之間按順序調(diào)用,實(shí)現(xiàn)A->B->C三個(gè)線程啟動(dòng)宋税,要求如下:

* AA打印5次摊崭,BB打印10次,CC打印15次

* 緊接著

* AA打印5次杰赛,BB打印10次呢簸,CC打印15次

* ......

* 來(lái)10輪

*/

public class SyncAndReentrantLockDemo {

? ? public static void main(String[] args) {

? ? ? ? ShareResource shareResource = new ShareResource();

? ? ? ? new Thread(() -> {

? ? ? ? ? ? for (int i = 1; i <= 10; i++) {

? ? ? ? ? ? ? ? shareResource.print5();

? ? ? ? ? ? }

? ? ? ? }, "AA").start();

? ? ? ? new Thread(() -> {

? ? ? ? ? ? for (int i = 1; i <= 10; i++) {

? ? ? ? ? ? ? ? shareResource.print10();

? ? ? ? ? ? }

? ? ? ? }, "BB").start();

? ? ? ? new Thread(() -> {

? ? ? ? ? ? for (int i = 1; i <= 10; i++) {

? ? ? ? ? ? ? ? shareResource.print15();

? ? ? ? ? ? }

? ? ? ? }, "CC").start();

? ? }

}


鏈表結(jié)構(gòu)與數(shù)組結(jié)構(gòu)?

不同:鏈表是鏈?zhǔn)降拇鎯?chǔ)結(jié)構(gòu);數(shù)組是順序的存儲(chǔ)結(jié)構(gòu)。鏈表通過(guò)指針來(lái)連接元素與元素根时,數(shù)組則是把所有元素按次序依次存儲(chǔ)瘦赫。鏈表的插入刪除元素相對(duì)數(shù)組較為簡(jiǎn)單,不需要移動(dòng)元素蛤迎,且較為容易實(shí)現(xiàn)長(zhǎng)度擴(kuò)充确虱,但是尋找某個(gè)元素較為困難;數(shù)組尋找某個(gè)元素較為簡(jiǎn)單忘苛,但插入與刪除比較復(fù)雜蝉娜,由于最大長(zhǎng)度需要再編程一開(kāi)始時(shí)指定,故當(dāng)達(dá)到最大長(zhǎng)度時(shí)扎唾,擴(kuò)充長(zhǎng)度不如鏈表方便。


2019 Java 底層面試題(第二篇) - 簡(jiǎn)書(shū)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末南缓,一起剝皮案震驚了整個(gè)濱河市胸遇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌汉形,老刑警劉巖纸镊,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異概疆,居然都是意外死亡逗威,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門岔冀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)凯旭,“玉大人,你說(shuō)我怎么就攤上這事使套」藓簦” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵侦高,是天一觀的道長(zhǎng)嫉柴。 經(jīng)常有香客問(wèn)我,道長(zhǎng)奉呛,這世上最難降的妖魔是什么计螺? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮瞧壮,結(jié)果婚禮上登馒,老公的妹妹穿的比我還像新娘。我一直安慰自己馁痴,他們只是感情好谊娇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般济欢。 火紅的嫁衣襯著肌膚如雪赠堵。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天法褥,我揣著相機(jī)與錄音茫叭,去河邊找鬼。 笑死半等,一個(gè)胖子當(dāng)著我的面吹牛揍愁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播杀饵,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼莽囤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了切距?” 一聲冷哼從身側(cè)響起朽缎,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谜悟,沒(méi)想到半個(gè)月后话肖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡葡幸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年最筒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔚叨。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡床蜘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出缅叠,到底是詐尸還是另有隱情悄泥,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布肤粱,位于F島的核電站弹囚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏领曼。R本人自食惡果不足惜鸥鹉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望庶骄。 院中可真熱鬧毁渗,春花似錦、人聲如沸单刁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至肺樟,卻和暖如春檐春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背么伯。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工疟暖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人田柔。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓俐巴,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親硬爆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子欣舵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355