JUC編程:Lock鎖-集合類(lèi)不安全-輔助類(lèi)

1 什么是JUC

java.util 工具包颅筋、包田轧、分類(lèi)

業(yè)務(wù):普通的線(xiàn)程代碼Thread

Runnable 沒(méi)有返回值儒洛、效率相比入 Callable 相對(duì)較低!

2 線(xiàn)程和進(jìn)程

  • 進(jìn)程:一個(gè)程序希太,QQ.exe Music.exe 程序的集合克饶;
  • 一個(gè)進(jìn)程往往可以包含多個(gè)線(xiàn)程,至少包含一個(gè)誊辉!
  • Java默認(rèn)有幾個(gè)線(xiàn)程矾湃? 2 個(gè) mian、GC
  • 線(xiàn)程:開(kāi)了一個(gè)進(jìn)程 Typora堕澄,寫(xiě)字邀跃,自動(dòng)保存(線(xiàn)程負(fù)責(zé)的)
  • 對(duì)于Java而言:Thread霉咨、Runnable、Callable
  • Java 真的可以開(kāi)啟線(xiàn)程嗎拍屑? 開(kāi)不了
public synchronized void start() {
    /**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    /* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
    group.add(this);
    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
        }
    }
}
// 本地方法途戒,底層的C++ ,Java 無(wú)法直接操作硬件
private native void start0();

2.1 并發(fā)僵驰、并行

并發(fā)編程:并發(fā)喷斋、并行

并發(fā)(多線(xiàn)程操作同一個(gè)資源)

  • CPU 一核 ,模擬出來(lái)多條線(xiàn)程蒜茴,天下武功星爪,唯快不破,快速交替

并行(多個(gè)人一起行走)

  • CPU 多核 粉私,多個(gè)線(xiàn)程可以同時(shí)執(zhí)行顽腾; 線(xiàn)程池
package com.kuang.demo01;
public class Test1 {
    public static void main(String[] args) {
        // 獲取cpu的核數(shù)
        // CPU 密集型,IO密集型
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

并發(fā)編程的本質(zhì):充分利用CPU的資源

線(xiàn)程有幾個(gè)狀態(tài)?

public enum State {
    // 新生
    NEW,
    // 運(yùn)行
    RUNNABLE,
    // 阻塞
    BLOCKED,
    // 等待诺核,死死地等
    WAITING,
    // 超時(shí)等待
    TIMED_WAITING,
    // 終止
    TERMINATED;
}

wait/sleep 區(qū)別

  1. 來(lái)自不同的類(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;
// 基本的賣(mài)票例子
import java.time.OffsetDateTime;
/**
* 真正的多線(xiàn)程開(kāi)發(fā)陈瘦,公司中的開(kāi)發(fā)幌甘,降低耦合性
* 線(xiàn)程就是一個(gè)單獨(dú)的資源類(lèi),沒(méi)有任何附屬的操作痊项!
* 1锅风、 屬性、方法
*/
public class SaleTicketDemo01 {
    public static void main(String[] args) {
        // 并發(fā):多線(xiàn)程操作同一個(gè)資源類(lèi), 把資源類(lèi)丟入線(xiàn)程
        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();
    }
}
// 資源類(lèi) OOP
class Ticket {
    // 屬性皱埠、方法
    private int number = 30;
    // 賣(mài)票的方式
    // synchronized 本質(zhì): 隊(duì)列,鎖
    public synchronized void sale(){
        if (number>0){
            System.out.println(Thread.currentThread().getName()+"賣(mài)出了"+(number-
                                                                       -)+"票,剩余:"+number);
        }
    }
}

Lock 接口

加鎖咖驮,解鎖操作

實(shí)現(xiàn)類(lèi):

可以選擇是否開(kāi)啟公平鎖

公平鎖:十分公平:可以先來(lái)后到

非公平鎖:十分不公平:可以插隊(duì) (默認(rèn))

  1. new ReentrantLock();
  2. lock.lock(); // 加鎖
  3. finally=> lock.unlock(); // 解鎖
package com.kuang.demo01;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTicketDemo02 {
    public static void main(String[] args) {
        // 并發(fā):多線(xiàn)程操作同一個(gè)資源類(lèi), 把資源類(lèi)丟入線(xiàn)程
        Ticket2 ticket = new Ticket2();
        // @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();
    }
}
// Lock三部曲
// 1、 new ReentrantLock();
// 2托修、 lock.lock(); // 加鎖
// 3忘巧、 finally=> lock.unlock(); // 解鎖
class Ticket2 {
    // 屬性、方法
    private int number = 30;
    Lock lock = new ReentrantLock();
    public void sale(){
        lock.lock(); // 加鎖
        try {
            // 業(yè)務(wù)代碼
            if (number>0){
                System.out.println(Thread.currentThread().getName()+"賣(mài)出了"+
                                   (number--)+"票,剩余:"+number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock(); // 解鎖
        }
    }
}

Synchronized 和 Lock 區(qū)別

  1. Synchronized 內(nèi)置的Java關(guān)鍵字睦刃, Lock 是一個(gè)Java類(lèi)
  2. Synchronized 無(wú)法判斷獲取鎖的狀態(tài)砚嘴,Lock可以判斷是否獲取到了鎖
  3. Synchronized會(huì)自動(dòng)釋放鎖,lock 必須要手動(dòng)釋放鎖!如果不釋放鎖际长,死鎖
  4. Synchronized線(xiàn)程 1(獲得鎖耸采,阻塞)、線(xiàn)程2(等待工育,傻傻的等)虾宇;Lock鎖就不一定會(huì)等待下去;
  5. Synchronized可重入鎖翅娶,不可以中斷的文留,非公平;Lock 竭沫,可重入鎖,可以 判斷鎖骑篙,非公平(可以
    自己設(shè)置)蜕提;
  6. Synchronized適合鎖少量的代碼同步問(wèn)題,Lock適合鎖大量的同步代碼靶端!

4. 生產(chǎn)者和消費(fèi)者問(wèn)題

生產(chǎn)者和消費(fèi)者問(wèn)題 Synchronized 版

package com.kuang.pc;
/**
* 線(xiàn)程之間的通信問(wèn)題:生產(chǎn)者和消費(fèi)者問(wèn)題谎势! 等待喚醒,通知喚醒
* 線(xiàn)程交替執(zhí)行 A B 操作同一個(gè)變量 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();
    }
}
// 判斷等待杨名,業(yè)務(wù)脏榆,通知
class Data{ // 數(shù)字 資源類(lèi)
    private int number = 0;
    //+1
    public synchronized void increment() throws InterruptedException {
        if (number!=0){ //0
            // 等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        // 通知其他線(xiàn)程,我+1完畢了
        this.notifyAll();
    }
    //-1
    public synchronized void decrement() throws InterruptedException {
        if (number==0){ // 1
            // 等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        // 通知其他線(xiàn)程台谍,我-1完畢了
        this.notifyAll();
    }
}

問(wèn)題存在须喂,A B C D 4 個(gè)線(xiàn)程! 虛假喚醒

原因:if是一次判斷

if 改為 while 判斷

/**
* 線(xiàn)程之間的通信問(wèn)題:生產(chǎn)者和消費(fèi)者問(wèn)題趁蕊! 等待喚醒坞生,通知喚醒
* 線(xiàn)程交替執(zhí)行 A B 操作同一個(gè)變量 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();
    }
}
// 判斷等待,業(yè)務(wù)掷伙,通知
class Data{ // 數(shù)字 資源類(lèi)
    private int number = 0;
    //+1
    public synchronized void increment() throws InterruptedException {
        while (number!=0){ //0
            // 等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        // 通知其他線(xiàn)程是己,我+1完畢了
        this.notifyAll();
    }
    //-1
    public synchronized void decrement() throws InterruptedException {
        while (number==0){ // 1
            // 等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        // 通知其他線(xiàn)程,我-1完畢了
        this.notifyAll();
    }
}

JUC版的生產(chǎn)者和消費(fèi)者問(wèn)題

通過(guò)Lock 找到 Condition

代碼實(shí)現(xiàn):

package com.kuang.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();
    }
}
// 判斷等待任柜,業(yè)務(wù)卒废,通知
class Data2{ // 數(shù)字 資源類(lèi)
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    //condition.await(); // 等待
    //condition.signalAll(); // 喚醒全部
    //+1
    public void increment() throws InterruptedException {
        任何一個(gè)新的技術(shù),絕對(duì)不是僅僅只是覆蓋了原來(lái)的技術(shù)宙地,優(yōu)勢(shì)和補(bǔ)充摔认!
            Condition 精準(zhǔn)的通知和喚醒線(xiàn)程
            lock.lock();
        try {
            // 業(yè)務(wù)代碼
            while (number!=0){ //0
                // 等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            // 通知其他線(xiàn)程,我+1完畢了
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    //-1
    public synchronized void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (number==0){ // 1
                // 等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            // 通知其他線(xiàn)程绸栅,我-1完畢了
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

任何一個(gè)新的技術(shù)级野,絕對(duì)不是僅僅只是覆蓋了原來(lái)的技術(shù),優(yōu)勢(shì)和補(bǔ)充!

Condition 精準(zhǔn)的通知和喚醒線(xiàn)程

代碼測(cè)試:

package com.kuang.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author 狂神說(shuō)Java 24736743@qq.com
* A 執(zhí)行完調(diào)用B蓖柔,B執(zhí)行完調(diào)用C辰企,C執(zhí)行完調(diào)用A
*/
public class C {
    public static void main(String[] args) {
        Data3 data = new Data3();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                data.printA();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                data.printB();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                data.printC();
            }
        },"C").start();
    }
}
class Data3{ // 資源類(lèi) Lock
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int number = 1; // 1A 2B 3C
    public void printA(){
        lock.lock();
        try {
            // 業(yè)務(wù),判斷-> 執(zhí)行-> 通知
            while (number!=1){
                // 等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>AAAAAAA");
            // 喚醒况鸣,喚醒指定的人牢贸,B
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB(){
        lock.lock();
        try {
            // 業(yè)務(wù),判斷-> 執(zhí)行-> 通知
            while (number!=2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBB");
            // 喚醒镐捧,喚醒指定的人潜索,c
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC(){
        lock.lock();
        try {
            // 業(yè)務(wù),判斷-> 執(zhí)行-> 通知
            // 業(yè)務(wù)懂酱,判斷-> 執(zhí)行-> 通知
            while (number!=3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBB");
            // 喚醒竹习,喚醒指定的人,c
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

5. 8鎖現(xiàn)象

如何判斷鎖的是誰(shuí)列牺!永遠(yuǎn)的知道什么鎖整陌,鎖到底鎖的是誰(shuí)!

8個(gè)問(wèn)題深刻理解我們的鎖

package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
* 8鎖瞎领,就是關(guān)于鎖的8個(gè)問(wèn)題
* 1泌辫、標(biāo)準(zhǔn)情況下,兩個(gè)線(xiàn)程先打印 發(fā)短信還是 打電話(huà)九默? 1/發(fā)短信 2/打電話(huà):1
* 1震放、sendSms延遲4秒,兩個(gè)線(xiàn)程先打印 發(fā)短信還是 打電話(huà)驼修? 1/發(fā)短信 2/打電話(huà):1
*/
public class Test1 {
    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 鎖的對(duì)象是方法的調(diào)用者殿遂!、
    // 兩個(gè)方法用的是同一個(gè)鎖邪锌,誰(shuí)先拿到誰(shuí)執(zhí)行勉躺!
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("發(fā)短信");
    }
    public synchronized void call(){
        System.out.println("打電話(huà)");
    }
}
package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
* 3、 增加了一個(gè)普通方法后觅丰!先執(zhí)行發(fā)短信還是Hello饵溅? 普通方法
* 4、 兩個(gè)對(duì)象妇萄,兩個(gè)同步方法蜕企, 發(fā)短信還是 打電話(huà)? // 打電話(huà)
*/
public class Test2 {
    public static void main(String[] args) {
        // 兩個(gè)對(duì)象冠句,兩個(gè)調(diào)用者轻掩,兩把鎖!
        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 鎖的對(duì)象是方法的調(diào)用者懦底!
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("發(fā)短信");
    }
    public synchronized void call(){
        System.out.println("打電話(huà)");
    }
    // 這里沒(méi)有鎖唇牧!不是同步方法,不受鎖的影響
    public void hello(){
        System.out.println("hello");
    }
}
package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
* 5、增加兩個(gè)靜態(tài)的同步方法丐重,只有一個(gè)對(duì)象腔召,先打印 發(fā)短信?打電話(huà)扮惦?:發(fā)短信
* 6臀蛛、兩個(gè)對(duì)象!增加兩個(gè)靜態(tài)的同步方法崖蜜, 先打印 發(fā)短信浊仆?打電話(huà)?:發(fā)短信
*/
public class Test3 {
    public static void main(String[] args) {
        // 兩個(gè)對(duì)象的Class類(lèi)模板只有一個(gè)豫领,static抡柿,鎖的是Class
        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();
    }
}
// Phone3唯一的一個(gè) Class 對(duì)象
class Phone3{
    // synchronized 鎖的對(duì)象是方法的調(diào)用者!
    // static 靜態(tài)方法
    // 類(lèi)一加載就有了氏堤!鎖的是Class
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("發(fā)短信");
    }
    public static synchronized void call(){
        System.out.println("打電話(huà)");
    }
}
package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
* 1沙绝、1個(gè)靜態(tài)的同步方法,1個(gè)普通的同步方法 鼠锈,一個(gè)對(duì)象,先打印 發(fā)短信星著?打電話(huà)购笆?:打電話(huà)
* 2、1個(gè)靜態(tài)的同步方法虚循,1個(gè)普通的同步方法 同欠,兩個(gè)對(duì)象,先打印 發(fā)短信横缔?打電話(huà)铺遂?:打電話(huà)
*/
public class Test4 {
    public static void main(String[] args) {
        // 兩個(gè)對(duì)象的Class類(lèi)模板只有一個(gè),static茎刚,鎖的是Class
        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();
    }
}
// Phone3唯一的一個(gè) Class 對(duì)象
class Phone4{
    // 靜態(tài)的同步方法 鎖的是 Class 類(lèi)模板
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("發(fā)短信");
    }
    // 普通的同步方法 鎖的調(diào)用者
    public synchronized void call(){
        System.out.println("打電話(huà)");
    }
}

6. 集合類(lèi)不安全

List 不安全

java.util.ConcurrentModificationException 并發(fā)修改異常襟锐!

解決方案:

  1. List<String> list = new Vector<>();
  2. List<String> list = Collections.synchronizedList(new ArrayList<>());
  3. List<String> list = new CopyOnWriteArrayList<>();
package com.kuang.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
// java.util.ConcurrentModificationException 并發(fā)修改異常膛锭!
public class ListTest {
    public static void main(String[] args) {
        // 并發(fā)下 ArrayList 不安全的嗎粮坞,Synchronized;
        /**
* 解決方案初狰;
* 1莫杈、List<String> list = new Vector<>();
* 2、List<String> list = Collections.synchronizedList(new ArrayList<>
());
* 3奢入、List<String> list = new CopyOnWriteArrayList<>()筝闹;
*/
        // CopyOnWrite 寫(xiě)入時(shí)復(fù)制 COW 計(jì)算機(jī)程序設(shè)計(jì)領(lǐng)域的一種優(yōu)化策略;
        // 多個(gè)線(xiàn)程調(diào)用的時(shí)候,list关顷,讀取的時(shí)候糊秆,固定的,寫(xiě)入(覆蓋)
        // 在寫(xiě)入的時(shí)候避免覆蓋解寝,造成數(shù)據(jù)問(wèn)題扩然!
        // 讀寫(xiě)分離
        // CopyOnWriteArrayList 比 Vector Nb 在哪里?
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

Set 不安全

package com.kuang.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* 同理可證 : ConcurrentModificationException
* //1聋伦、Set<String> set = Collections.synchronizedSet(new HashSet<>());
* //2夫偶、
*/
public class SetTest {
    public static void main(String[] args) {
        // Set<String> set = new HashSet<>();
        // Set<String> set = Collections.synchronizedSet(new HashSet<>());
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 1; i <=30 ; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

hashSet 底層是什么?

public HashSet() {
    map = new HashMap<>();
}
// add set 本質(zhì)就是 map key是無(wú)法重復(fù)的觉增!
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object(); // 不變得值兵拢!

Map 不安全

回顧Map基本操作

package com.kuang.unsafe;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
// ConcurrentModificationException
public class MapTest {
    public static void main(String[] args) {
        // map 是這樣用的嗎? 不是逾礁,工作中不用 HashMap
        // 默認(rèn)等價(jià)于什么说铃? new HashMap<>(16,0.75);
        // Map<String, String> map = new HashMap<>();
        // 唯一的一個(gè)家庭作業(yè):研究ConcurrentHashMap的原理
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 1; i <=30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(
                    0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

7. Callable ( 簡(jiǎn)單 )

  1. 可以有返回值
  2. 可以?huà)伋霎惓?/li>
  3. 方法不同,run()/ call()

代碼測(cè)試

Runnable實(shí)現(xiàn)類(lèi)

package com.kuang.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.ReentrantLock;
/**
* 1嘹履、探究原理
* 2腻扇、覺(jué)自己會(huì)用
*/
public class CallableTest {
    public static void main(String[] args) throws ExecutionException,
    InterruptedException {
        // new Thread(new Runnable()).start();
        // new Thread(new FutureTask<V>()).start();
        // new Thread(new FutureTask<V>( Callable )).start();
        new Thread().start(); // 怎么啟動(dòng)Callable
        MyThread thread = new MyThread();
        FutureTask futureTask = new FutureTask(thread); // 適配類(lèi)
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start(); // 結(jié)果會(huì)被緩存,效率高
        Integer o = (Integer) futureTask.get(); //這個(gè)get 方法可能會(huì)產(chǎn)生阻塞砾嫉!把他放到
        最后
            // 或者使用異步通信來(lái)處理幼苛!
            System.out.println(o);
    }
}
class MyThread implements Callable<Integer> {
    @Override
    public Integer call() {
        System.out.println("call()"); // 會(huì)打印幾個(gè)call
        // 耗時(shí)的操作
        return 1024;
    }
}

細(xì)節(jié):

  1. 有緩存
  2. 結(jié)果可能需要等待,會(huì)阻塞焕刮!

8. 常用的輔助類(lèi)(必會(huì))

8.1 CountDownLatch

package com.kuang.add;
import java.util.concurrent.CountDownLatch;
// 計(jì)數(shù)器
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        // 總數(shù)是6舶沿,必須要執(zhí)行任務(wù)的時(shí)候,再使用配并!
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <=6 ; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" Go out");
                countDownLatch.countDown(); // 數(shù)量-1
            },String.valueOf(i)).start();
        }
        countDownLatch.await(); // 等待計(jì)數(shù)器歸零括荡,然后再向下執(zhí)行
        System.out.println("Close Door");
    }
}

原理:

  • countDownLatch.countDown();// 數(shù)量-1
  • countDownLatch.await(); // 等待計(jì)數(shù)器歸零,然后再向下執(zhí)行
  • 每次有線(xiàn)程調(diào)用 countDown()數(shù)量-1溉旋,假設(shè)計(jì)數(shù)器變?yōu)?畸冲,countDownLatch.await()就會(huì)被喚醒,繼續(xù)
    執(zhí)行低滩!

8.2 CyclicBarrier

加法計(jì)數(shù)器

package com.kuang.add;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        /**
* 集齊7顆龍珠召喚神龍
*/
        // 召喚龍珠的線(xiàn)程
        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+"個(gè)龍珠");
                                   try {
                                       cyclicBarrier.await(); // 等待
                                   } catch (InterruptedException e) {
                                       e.printStackTrace();
                                   } catch (BrokenBarrierException e) {
                                       e.printStackTrace();
                                   }
                                   }).start();
            }
     }
}                     

8.3 Semaphore

Semaphore:信號(hào)量

搶車(chē)位!

6車(chē)---3個(gè)停車(chē)位置

package com.kuang.add;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
    public static void main(String[] args) {
        // 線(xiàn)程數(shù)量:停車(chē)位! 限流恕沫!
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <=6 ; i++) {
            new Thread(()->{
                // acquire() 得到
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"搶到車(chē)                                       位");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"離開(kāi)車(chē)                                       位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // release() 釋放
                }
            },String.valueOf(i)).start();
        }
    }
}

原理:

  • semaphore.acquire() 獲得监憎,假設(shè)如果已經(jīng)滿(mǎn)了,等待婶溯,等待被釋放為止鲸阔!
  • semaphore.release();釋放偷霉,會(huì)將當(dāng)前的信號(hào)量釋放 + 1,然后喚醒等待的線(xiàn)程褐筛!
  • 作用: 多個(gè)共享資源互斥的使用类少!并發(fā)限流,控制最大的線(xiàn)程數(shù)渔扎!

參考:B站狂神說(shuō)Java

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末硫狞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子晃痴,更是在濱河造成了極大的恐慌残吩,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倘核,死亡現(xiàn)場(chǎng)離奇詭異泣侮,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)紧唱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)活尊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人漏益,你說(shuō)我怎么就攤上這事蛹锰。” “怎么了绰疤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵宁仔,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我峦睡,道長(zhǎng),這世上最難降的妖魔是什么权埠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任榨了,我火速辦了婚禮,結(jié)果婚禮上攘蔽,老公的妹妹穿的比我還像新娘龙屉。我一直安慰自己,他們只是感情好满俗,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布转捕。 她就那樣靜靜地躺著,像睡著了一般唆垃。 火紅的嫁衣襯著肌膚如雪五芝。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,549評(píng)論 1 312
  • 那天辕万,我揣著相機(jī)與錄音枢步,去河邊找鬼沉删。 笑死,一個(gè)胖子當(dāng)著我的面吹牛醉途,可吹牛的內(nèi)容都是我干的矾瑰。 我是一名探鬼主播,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼隘擎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼殴穴!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起货葬,我...
    開(kāi)封第一講書(shū)人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤采幌,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后宝惰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體植榕,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年尼夺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了尊残。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡淤堵,死狀恐怖寝衫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拐邪,我是刑警寧澤慰毅,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站扎阶,受9級(jí)特大地震影響汹胃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜东臀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一着饥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惰赋,春花似錦宰掉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至拒炎,卻和暖如春挪拟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背枝冀。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工舞丛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耘子,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓球切,卻偏偏與公主長(zhǎng)得像谷誓,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吨凑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容