Java之JUC併發編程

JUC:java util concurrent
在這裏插入圖片描述
普通的線程代碼 Thread
Runnable:沒有返回值 效率相比Callable較低

線程和進程

線程、進程

進程:一個程序,QQ.exe Music.exe 程序的集合
一個進程可以包含多個線程,至少包含一個
java默認有幾個線程? 2個 main線程+GC線程
線程:Thread Runnable Callable
Java真的可以開啓線程麼 開不了,只能調用native本地方法,Java無法操作硬件,因爲Java運行在虛擬機上

    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 */
            }
        }
    }

    private native void start0();

併發、並行

併發:多線程同時操作同一資源
並行:多個人一起行走 CPU多核,多個線程同時執行

併發編程的本質:充分利用CPU的資源4

線程有幾個狀態

 public enum State {
         //新生
        NEW,

         //運行
        RUNNABLE,

         //阻塞
        BLOCKED,

         //等待
        WAITING,

         //超時等待
        TIMED_WAITING,

         //終止
        TERMINATED;
    }

wati/sleep的區別

1.來自不同的類
wait=>Object
sleep=>Thread
2.關於鎖的釋放
wait釋放鎖,sleep不會釋放鎖
3.使用的範圍不同
wait:必須在同步代碼塊中
sleep:任何地方
4.是否需要捕獲異常
wait:不需要捕獲異常
sleep:需要捕獲異常

Lock鎖

傳統Synchronized

/**
 * @Description: 基本的賣票例子
 * @Date: 2020-05-23 17:00
 **/

/**
 * 線程就是一個單獨的資源類,沒有任何附屬的操作
 * 1、 屬性 方法
 */
public class SaleTicketDemo01 {
    public static void main(String[] args) {
        //併發 多線程操作同一個資源類
        Ticket ticket = new Ticket();

        new Thread(() -> {
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        }, "C").start();
    }
}

//資源OOP
class Ticket {
    //屬性 方法
    private int number = 50;

    //賣票的方式
    public synchronized void sale() {
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "賣出了第" + (number--) + "票");
        }
    }
}

Lock接口

在這裏插入圖片描述
在這裏插入圖片描述

    public ReentrantLock() {
        sync = new NonfairSync(); //非公平鎖
    }
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

公平鎖:先來後到
非公平鎖:可以插隊(根據CPU),默認非公平鎖
(公平鎖不公平,非公平鎖公平,因爲如果有兩個線程,一個執行3s 一個執行3h,3h的先到,那麼公平鎖就會讓3s的線程等待)

public class SaleTicketDemo02 {
    public static void main(String[] args) {
        //併發 多線程操作同一個資源類
        Ticket02 ticket = new Ticket02();

        new Thread(() -> { for (int i = 0; i < 60; i++) ticket.sale(); }, "A").start();
        new Thread(() -> { for (int i = 0; i < 60; i++) ticket.sale(); }, "B").start();
        new Thread(() -> { for (int i = 0; i < 60; i++) ticket.sale(); }, "C").start();
    }
}
//Lock三部曲
//new ReentrantLock()
//lock.lock()
//try catch finally => lock.unlock
//資源OOP
class Ticket02 {
    //屬性 方法
    private int number = 50;

    Lock lock = new ReentrantLock();

    //賣票的方式
    public void sale() {

        lock.lock();

        try {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "賣出了第" + (number--) + "票");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Synchronized 和 Lock的區別

1、Synchronized是關鍵字 Lock是類
2、Synchronized無法判斷獲取鎖的狀態,Lock可以判斷是否獲取到了鎖
3、Synchronized會自動釋放鎖,Lock鎖必須手動釋放鎖,如果不釋放鎖會死鎖
4、Synchronized 線程1獲得鎖,線程2等待,當線程1阻塞是,線程2一直等;Lock鎖不一定會等待下去
5、Synchronized可重入鎖,不可以中斷,非公平;Lock可重入鎖,可以判斷鎖,可以自己設置公平鎖和非公平鎖,默認非公平
6、Synchronized適合鎖少量的代碼同步問題,Lock適合鎖大量的同步代碼

鎖是什麼?如何判斷鎖的是什麼

生產者和消費者問題

Synchronized版本

/**
 * @Description: 線程之間的通信問題 生產者和消費者問題
 * 線程交替執行 A B操作同一個變量 num=0
 * A num+1
 * B num-1
 * @Author: wangyinghao_sx
 * @Date: 2020-05-23 17:46
 **/
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();
    }
}

// 判斷等待 業務 通知
class Data {
    private int number = 0;

    public synchronized void increment() throws InterruptedException {
    	//if (number != 0) {
            //等待
            //wait();
        //}
        // 如果用if 多個生產者或者消費者的時候會有問題 
        while (number != 0) {
            //等待
            wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
        // 通知其他線程 我+1完畢
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
    	//if (number == 0) {
            //等待
            //wait();
        //}
        while (number == 0) {
            //等待
            wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
        // 通知其他線程,我-1完畢
        this.notifyAll();
    }
}

問題存在:多個消費者 生產者時使用if導致的虛假喚醒的問題 所以將if改成while
在這裏插入圖片描述
可是爲什麼加了synchronized還會有兩個生產者都進入生產呢?原因是 當A生產之後,即當前number爲1,此時notifyAll,這個時候ABC都有機會被喚醒,假設C先被喚醒,之後if(number!=0) wait等待被喚醒,此時A再次醒來,A也if(number!=0) wait等待被喚醒,當B消費之後,number=0,但此時AC均已經判斷過number的值,所以如果接下來先喚醒A,那麼number+1,然後notifyAll喚醒了C,C再次number+1,所以會出現以下錯誤情況

A=>1
B=>0
A=>1
C=>2
A=>3
B=>2
B=>1
B=>0

改成while之後的正確結果

A=>1
B=>0
A=>1
B=>0
C=>1
B=>0
A=>1
B=>0
C=>1

JUC版生產者消費者
在這裏插入圖片描述
在這裏插入圖片描述

public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
    }
}

// 判斷等待 業務 通知
class Data {
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    public void increment() throws InterruptedException {
        try {
            lock.lock();
            // 如果用if 多個生產者的時候就會有問題
            while (number != 0) {
                //等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            // 通知其他線程 我+1完畢
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void decrement() throws InterruptedException {
        try {
            lock.lock();
            while (number == 0) {
                //等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Condition 可以精準通知想喚醒的進程

public class C {
    public static void main(String[] args) {
        Data03 data = new Data03();
        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                data.printA();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                data.printB();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                data.printC();
            }
        }, "C").start();
    }
}

// 判斷等待 業務 通知
class Data03 {
    private int number = 1;
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();

    public void printA() {
        try {
            lock.lock();
            while (number != 1) {
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName());
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB() {
        try {
            lock.lock();
            while (number != 2) {
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName());
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC() {
        try {
            lock.lock();
            while (number != 3) {
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName());
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
A
B
C
A
B
C
A
B
C

8鎖現象

如何判斷鎖的是誰

/**
 * @Description: 8鎖 就是關於鎖的八個問題
 * 1. 標準情況下 先發短信 後打電話
 * 2. sendSms延遲四秒 先發短信 後打電話
 * @Author: wangyinghao_sx
 * @Date: 2020-05-23 20:48
 **/
public class Test01 {
    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鎖的對象是方法的調用者
    //兩個方法用的是同個鎖 誰先拿到誰先執行
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("發短信");
    }

    public synchronized void call() {
        System.out.println("打電話");
    }
}

/**
 * @Description: 8鎖 就是關於鎖的八個問題
 * 3. 增加一個普通方法 先hello 再發短信
 * 4. 兩個對象 兩個同步方法 先打電話 再發短信 (發短信的sleep了)
 * @Author: wangyinghao_sx
 * @Date: 2020-05-23 20:48
 **/
public class Test02 {
    public static void main(String[] args) {
        Phone02 phone1 = new Phone02();
        Phone02 phone2 = new Phone02();

        new Thread(() -> {
            phone1.sendSms();
        }, "A").start();

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

        new Thread(() -> {
            phone2.call();
        }, "B").start();
    }
}

class Phone02 {
    //synchronized鎖的對象是方法的調用者
    //兩個方法用的是同個鎖 誰先拿到誰先執行
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("發短信");
    }

    public synchronized void call() {
        System.out.println("打電話");
    }

    public void hello(){
        System.out.println("hello");
    }
}

/**
 * @Description: 8鎖 就是關於鎖的八個問題
 * 5. 增加兩個靜態的同步方法 只有一個對象 先打印發短信 後打電話
 * 6. 兩個對象,增加兩個靜態的同步方法 先發短信 後打電話
 * @Author: wangyinghao_sx
 * @Date: 2020-05-23 20:48
 **/
public class Test03 {
    public static void main(String[] args) {
        Phone03 phone1 = new Phone03();
        Phone03 phone2 = new Phone03();

        new Thread(() -> {
            phone1.sendSms();
        }, "A").start();

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

        new Thread(() -> {
            phone2.call();
        }, "B").start();
    }
}

class Phone03 {
    //synchronized鎖的對象是方法的調用者
    //static 靜態方法
    //類一加載就有了 鎖的是Class模版
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("發短信");
    }

    public static synchronized void call() {
        System.out.println("打電話");
    }
}

/**
 * @Description: 8鎖 就是關於鎖的八個問題
 * 5. 一個普通同步方法 一個靜態同步方法 只有一個對象 先打印打電話 後發短信
 * 6. 兩個對象,一個普通同步方法 一個靜態同步方法 先打印打電話 後發短信
 * @Author: wangyinghao_sx
 * @Date: 2020-05-23 20:48
 **/
public class Test04 {
    public static void main(String[] args) {
        Phone04 phone1 = new Phone04();
        Phone04 phone2 = new Phone04();

        new Thread(() -> {
            phone1.sendSms();
        }, "A").start();

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

        new Thread(() -> {
            phone2.call();
        }, "B").start();
    }
}

class Phone04 {
    //synchronized鎖的對象是方法的調用者
    //static 靜態方法
    //類一加載就有了 鎖的是Class模版
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("發短信");
    }

    //普通同步方法 鎖的是調用者
    public synchronized void call() {
        System.out.println("打電話");
    }
}

List不安全

/**
 * @Description: java.util.ConcurrentModificationException
 * @Author: wangyinghao_sx
 * @Date: 2020-05-24 10:17
 **/
public class ListTest {
    public static void main(String[] args) {
        /**
         * 併發下ArrayList不安全
         * 解決方案:
         * 1.List<String> list = new Vector<>();
         * 2.List<String> list = Collections.synchronizedList(new ArrayList<>());
         * 3.List<String> list = new CopyOnWriteArrayList<>();
         */

        //CopyOnWrite 寫入時複製 COW 計算機程序設計領域的一種優化策略
//        List<String> list = new CopyOnWriteArrayList<>();
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

Set 不安全

public class SetTest {
    public static void main(String[] args) {

        //1.Set<String> set = Collections.synchronizedSet(new HashSet<>());
        //2.Set<String> set = new CopyOnWriteArraySet<>();

        Set<String> set = new HashSet<>();
        for (int i = 0; i < 10; 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本質就是map的key是無法重複的
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

Map不安全

public class MapTest {
    public static void main(String[] args) {
//         Map<String, String> map = new ConcurrentHashMap<>();
        Map<String, String> map = new HashMap<>();
        // 加載因子 static final float DEFAULT_LOAD_FACTOR = 0.75f;
        // 初始化容量 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }
}

Callable

1、可以有返回值
2、可以拋出異常
3、和Runnable方法不同 run->call

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        new Thread(new MyThread()).start();

        MyCallable callable = new MyCallable();
        FutureTask futureTask = new FutureTask(callable);
        new Thread(futureTask, "A").start();
        new Thread(futureTask, "B").start(); //兩個Thread只返回了一個123  結果有緩存 效率高
        String o = (String) futureTask.get(); //獲取Callable的返回結果 get可能產生阻塞
        //把get放到最後 或者異步通信
        System.out.println(o);
    }
}

class MyThread implements Runnable {
    @Override
    public void run() {

    }
}

class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "123";
    }
}

常用的輔助類

CountDownLatch 減法計數器
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        //總數是6
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName());
                countDownLatch.countDown();
            }, String.valueOf(i)).start();
        }
        countDownLatch.await(); //等待計數器歸零 再向下繼續執行
        System.out.println("OK");
    }
}
CyclicBarrier 加法計數器
public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("OK");
        });
        for (int i = 0; i < 7; i++) {
            // lambda能操作到for循環的i麼 不能 lambda本質是一個類 除非i設置爲final或jdk8的effecitvely final
            int finalI = i; //jdk8的優化 默認應該是final int finalI = i
            new Thread(() -> {
                System.out.println(finalI);
                try {
                    cyclicBarrier.await(); //等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}
Semaphore 信號量
public class SemaphoreDemo {
    public static void main(String[] args) {
        // 允許運行的線程數量
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                // acquire 得到
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "搶到車位");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName() + "離開車位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // release釋放
                    semaphore.release();
                }
            }, String.valueOf(i)).start();
        }
    }
}

原理:
semaphore.acquire(); 獲得,假設如果已經滿了,等待
semaphore.release(); 釋放當前信號量,喚醒等待線程

讀寫鎖 ReadWriteLock

public class ReadWriteLockDemo {
    public static void main(String[] args) {
//        MyCache myCache = new MyCache();
//        for (int i = 0; i < 10; i++) {
//            int finalI = i;
//            new Thread(() -> {
//                myCache.put(finalI + "", finalI + "");
//            }, String.valueOf(i)).start();
//        }
//
//        for (int i = 0; i < 10; i++) {
//            int finalI = i;
//            new Thread(() -> {
//                myCache.get(finalI + "");
//            }, String.valueOf(i)).start();
//        }

        MyCacheLock myCacheLock = new MyCacheLock();
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                myCacheLock.put(finalI + "", finalI + "");
            }, String.valueOf(i)).start();
        }

        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                myCacheLock.get(finalI + "");
            }, String.valueOf(i)).start();
        }
    }
}

class MyCache {
    private volatile Map<String, Object> map = new HashMap<>();

    public void put(String key, Object value) {
        System.out.println(Thread.currentThread().getName() + "寫入" + key);
        map.put(key, value);
        System.out.println(Thread.currentThread().getName() + "寫入" + key + "完畢");
    }

    public void get(String key) {
        System.out.println(Thread.currentThread().getName() + "讀取" + key);
        map.get(key);
        System.out.println(Thread.currentThread().getName() + "讀取" + key + "完畢");

    }
}

//加鎖的緩存
class MyCacheLock {
    private volatile Map<String, Object> map = new HashMap<>();
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    //寫入的時候只希望同時一個線程寫 獨佔鎖
    public void put(String key, Object value) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "寫入" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "寫入" + key + "完畢");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    public void get(String key) {
        //讀取的時候所有人都可以讀 但是不能寫 共享鎖
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "讀取" + key);
            map.get(key);
            System.out.println(Thread.currentThread().getName() + "讀取" + key + "完畢");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }

    }
}

阻塞隊列 BlockingQueue

寫入的時候:如果隊列滿了,必須阻塞等待
讀取的時候:如果隊列是空的,必須阻塞等待‘

隊列有四組API:添加 刪除 查看隊首元素
1、拋出異常 add() remove() element()
2、不拋出異常,有返回值 offer() poll() peek()
3、阻塞等待,一直等待 put() take()
4、超時等待 offer(timeout,TimeUtil) pool(time,TimeUtil)

public class Test {
    public static void main(String[] args) {


    }

    // 拋出異常
    public static void test1() {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        System.out.println(blockingQueue.add("d"));
        System.out.println(blockingQueue.element());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
    }

    // 不拋出異常
    public static void test2() {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.offer("d"));
        System.out.println(blockingQueue.peek());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
    }

    // 等待阻塞 一直等待
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        blockingQueue.put("d");

        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
    }

    // 超時等待
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.offer("a");
        blockingQueue.offer("b");
        blockingQueue.offer("c");
        blockingQueue.offer("d", 2, TimeUnit.SECONDS);

        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
    }
}

SynchronousQueue 同步隊列

沒有容量,進去一個元素,必須等待取出來之後才能再放元素 put() take()

public class SynchronousQueueDemo {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();
        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName()+"put 1");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName()+"put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName()+"put 3");
                blockingQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T1").start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+blockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T2").start();
    }
}
T1put 1
T2take 1
T1put 2
T2take 2
T1put 3
T2take 3

線程池

線程池好處:

  • 降低資源的消耗
  • 提高響應速度
  • 方便管理
    即 線程複用,可以控制最大併發數,管理線程
    線程池:三大方法 七大參數 四種拒絕策略

三大方法

public class Demo01 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newSingleThreadExecutor();//單個線程
//        ExecutorService pool = Executors.newFixedThreadPool(5); //固定線程池大小
//        ExecutorService pool = Executors.newCachedThreadPool(); //可伸縮

        try {
            for (int i = 0; i < 10; i++) {
                pool.execute(() -> {
                    System.out.println(Thread.currentThread().getName());
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pool.shutdown();
        }
    }
}

七大參數

源碼分析

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
        public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
// 本質 ThreadPoolExecutor
    public ThreadPoolExecutor(int corePoolSize, 核心線程池大小
                              int maximumPoolSize, 最大線程池大小
                              long keepAliveTime, 存活時間,超時沒人調用會釋放
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory, 線程工廠 創建線程 一般不用動
                              RejectedExecutionHandler handler 拒絕策略) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

在這裏插入圖片描述

手動創建線程池

public class Demo01 {
    public static void main(String[] args) {
        //手動創建線程池
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,
                5,
                3, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());//銀行滿了 還有人進來 不處理這個人並拋出異常
        try {
            //最大承載等於 maxPoolSize+linkedBlockingDeque.size
            for (int i = 0; i <10; i++) {
                pool.execute(() -> {
                    System.out.println(Thread.currentThread().getName());
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pool.shutdown();
        }
    }
}

四種拒絕策略
四種拒絕策略

  • AbortPolicy 銀行滿了還進來人 不處理這個 並拋出異常
  • CallerRunsPolicy 拿來的回哪去 main會處理這個
  • DiscardOldestPolicy 隊列滿了嘗試和最早的任務競爭 不會拋出異常
  • DiscardPolicy 隊列滿了丟掉任務 不拋出異常

最大線程數應該怎麼定義:

  1. CPU密集型 幾核就定義幾 Runtime.getRuntime().availableProcessors()
  2. IO密集型 判斷程序中耗IO的線程個數 設置爲2倍

四大函數式接口

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

函數式接口簡化編程模型,在新版本的底層框架中大量應用
foreach(消費者類的函數式接口)

/**
 * @Description: 函數式接口 有一個輸入參數 有一個輸出
 * 函數式接口可以用lambda簡化
 * @Date: 2020-05-25 13:04
 **/
public class Demo01 {
    public static void main(String[] args) {
        Function<String, String> function = new Function<String, String>() {
            @Override
            public String apply(String str) {
                return str;
            }
        };

        function = (str) -> {
            return str;
        };
        System.out.println(function.apply("test"));
    }
}
/**
 * @Description: 斷定型接口 有一個輸入參數 返回值只能是布爾值
 * @Author: wangyinghao_sx
 * @Date: 2020-05-25 13:13
 **/
public class Demo02 {
    public static void main(String[] args) {
        Predicate<String> predicate = new Predicate<String>() {
            //判斷字符串是否爲空
            @Override
            public boolean test(String s) {
                return s.isEmpty();
            }
        };
        predicate = (s)->{
            return s.isEmpty();
        };
        System.out.println(predicate.test(""));
    }
}
/**
 * @Description: 消費型接口 只有輸入 沒有返回值
 * @Author: wangyinghao_sx
 * @Date: 2020-05-25 13:37
 **/
public class Demo03 {
    public static void main(String[] args) {
        Consumer<String> consumer = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        consumer = (s)->{
            System.out.println(s);
        };
        consumer.accept("test");
    }
}
/**
 * @Description: 供給型接口 沒有參數輸入 只有返回值
 * @Date: 2020-05-25 13:41
 **/
public class Demo04 {
    public static void main(String[] args) {
        Supplier<Integer> supplier = new Supplier<Integer>() {
            @Override
            public Integer get() {
                return 1024;
            }
        };
        supplier = () -> {
            return 1024;
        };
        System.out.println(supplier.get());
    }
}

stream流式計算

什麼是流式計算

大數據:存儲+計算
集合:MySQL本質就是存儲
計算都應該交給流來操作

public class Test {
    public static void main(String[] args) {
        User u1 = new User(1, "a", 21);
        User u2 = new User(2, "b", 22);
        User u3 = new User(3, "c", 23);
        User u4 = new User(4, "d", 24);
        User u5 = new User(6, "e", 25);
        //集合存儲
        List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
        //stream計算
        list.stream().filter(u->{return u.getId()%2==0;})
                .filter(u->{return u.getAge()>23;})
                .map(u->{return u.getName().toUpperCase();})
                .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
                .limit(1)
                .forEach(System.out::println);
    }
}

分枝合併 ForkJoin

並行執行任務,提高效率,大數據量
ForkJoin
ForkJoin特點:
工作竊取 若存在AB兩個線程同時處理多個任務,B線程先處理完所有任務,則B會幫A處理
原因:AB維護的是雙端隊列

/**
 * @Description: 求和計算
 * ForkJoin
 * stream並行計算
 * ForkJoin如何使用
 * 1、forkjoinPool 通過它來執行
 * 2、計算任務 forkjoinPool.execute(ForkJoinTask task)
 * 3、計算類繼承ForkJoinTask
 * @Author: wangyinghao_sx
 * @Date: 2020-05-25 15:04
 **/
public class Demo extends RecursiveTask<Long> {
    public Long start;
    public Long end;
    public Long temp = 10000L;

    public Demo(Long start, Long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        if (start - end < temp) {
            Long sum = 0L;
            for (Long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        } else {
            //forkjoin
            long mid = (start + end) / 2;
            Demo task1 = new Demo(start, mid);
            task1.fork(); //拆分任務 把任務壓入線程隊列
            Demo task2 = new Demo(mid, end);
            task2.fork();
            return task1.join() + task2.join();
        }
    }
}

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        test1();
        test2();
        test3();
        //7084
        //5668
        //202
    }

    //普通
    public static void test1() {
        Long sum = 0L;
        long start = System.currentTimeMillis();
        for (Long i = 1L; i <= 10_0000_0000; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

    //forkjoin
    public static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new Demo(1L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        Long sum = submit.get();
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

    //stream並行流
    public static void test3() {
        long start = System.currentTimeMillis();
        long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

異步回調

/**
 * @Description: 異步調用
 * @Author: wangyinghao_sx
 * @Date: 2020-05-25 15:45
 **/
public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //無返回值
        CompletableFuture<Void> completableFuture1 = CompletableFuture.runAsync(()->{
            System.out.println(Thread.currentThread().getName());
        });
        completableFuture1.get();

        //有返回值
        CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName());
            return 1024;
        });

        System.out.println(completableFuture2.whenComplete((t,u)->{
            System.out.println(t); //正常的返回結果
            System.out.println(u); //錯誤信息
        }).exceptionally((e)->{
            System.out.println(e.getMessage());
            return 233;
        }).get());
    }
}

JMM

請你談談volatile的理解

volatile是java虛擬機提供的輕量級同步機制
1.保證可見性
2.不保證原子性
3.禁止指令重排

什麼是JMM java內存模型(不存在 是一個概念)

  1. 線程解鎖前,必須把共享變量立刻刷回主存
  2. 線程加鎖前,必須讀取主存中的最新值到工作內存中
  3. 加鎖和解鎖是同一把鎖

線程:工作內存 主內存
內存交互操作有8種,虛擬機實現必須保證每一個操作都是原子的,不可在分的(對於double和long類型的變量來說,load、store、read和write操作在某些平臺上允許例外)

lock (鎖定):作用於主內存的變量,把一個變量標識爲線程獨佔狀態
unlock (解鎖):作用於主內存的變量,它把一個處於鎖定狀態的變量釋放出來,釋放後的變量纔可以被其他線程鎖定
read (讀取):作用於主內存變量,它把一個變量的值從主內存傳輸到線程的工作內存中,以便隨後的load動作使用
load (載入):作用於工作內存的變量,它把read操作從主存中變量放入工作內存中
use (使用):作用於工作內存中的變量,它把工作內存中的變量傳輸給執行引擎,每當虛擬機遇到一個需要使用到變量的值,就會使用到這個指令
assign (賦值):作用於工作內存中的變量,它把一個從執行引擎中接受到的值放入工作內存的變量副本中
store (存儲):作用於主內存中的變量,它把一個從工作內存中一個變量的值傳送到主內存中,以便後續的write使用
write  (寫入):作用於主內存中的變量,它把store操作從工作內存中得到的變量的值放入主內存的變量中

JMM對這八種指令的使用,制定瞭如下規則:

不允許read和load、store和write操作之一單獨出現。即使用了read必須load,使用了store必須write
不允許線程丟棄他最近的assign操作,即工作變量的數據改變了之後,必須告知主存
不允許一個線程將沒有assign的數據從工作內存同步回主內存
一個新的變量必須在主內存中誕生,不允許工作內存直接使用一個未被初始化的變量。就是懟變量實施use、store操作之前,必須經過assign和load操作
一個變量同一時間只有一個線程能對其進行lock。多次lock後,必須執行相同次數的unlock才能解鎖
如果對一個變量進行lock操作,會清空所有工作內存中此變量的值,在執行引擎使用這個變量前,必須重新load或assign操作初始化變量的值
如果一個變量沒有被lock,就不能對其進行unlock操作。也不能unlock一個被其他線程鎖住的變量
對一個變量進行unlock操作之前,必須把此變量同步回主內存

volatile保證可見性

public class JMMDemo {
//線程A 死循環 不加volatile感知不到num被修改
    private static int num = 0;
// 加volatile之後A能感知到main對num的修改 結束線程A
//    private static volatile int num = 0;

    public static void main(String[] args) {
        new Thread(() -> {
            while (num == 0) {

            }
        },"A).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
    }
}

volatile不保證原子性

原子性:不可分割,要麼同時成功,要麼同時失敗

如果不加lock或synchronized怎麼保證原子性
使用原子類解決原子性問題

public class VDemo02 {

    //AtomicInteger 原子類
    private volatile static AtomicInteger num = new AtomicInteger();

    public static void add() {
        num.getAndIncrement();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();

        }

        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println("OK" + num);
    }
}

    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

原子類的底層和操作系統掛鉤,在內存中修改值,Unsafe類是一個特殊的存在

指令重排

源代碼->編譯器優化重排->指令並行重排->內存系統重排->執行
在這裏插入圖片描述
volatile避免指令重排
內存屏障、CPU指令作用:

  1. 保證特定的操作的執行順序
  2. 可以保證某些變量的內存可見性

單例模式

餓漢式 DCL懶漢式

/**
 * @Description: 餓漢式單例 耗內存 原因:啓動時就加載了 如果類中有耗費內存的對象就會浪費空間
 * @Author: wangyinghao_sx
 * @Date: 2020-05-26 13:39
 **/
public class Hungry {
    private Hungry() {

    }

    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance() {
        return HUNGRY;
    }
}
/**
 * @Description: 餓漢式單例 單線程有效 多線程存在問題
 * @Author: wangyinghao_sx
 * @Date: 2020-05-26 13:44
 **/
public class LazyMan {

    private LazyMan(){

    }

    private static LazyMan lazyMan;

    public static LazyMan getInstance(){
        if(lazyMan==null){
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }
}
/**
 * @Description: 餓漢式單例 多線程安全 但是不管怎麼處理 只要沒用枚舉 反射都能破壞單例
 * @Author: wangyinghao_sx
 * @Date: 2020-05-26 13:44
 **/
public class SafeLazyMan {

    private SafeLazyMan(){

        //防止在反射的作用下不單例
        synchronized (LazyMan.class){
            if(lazyMan!=null){
                throw new RuntimeException("不要使用反射試圖破壞單例");
            }
        }
    }

//    private static SafeLazyMan lazyMan;
    // 因爲new SafeLazyMan()可能指令重排 所以爲了避免指令重排導致return null 加volatile
    private volatile static SafeLazyMan lazyMan;


    //雙重檢測鎖模式 DCL懶漢式
    public static SafeLazyMan getInstance(){
        if(lazyMan==null){
            synchronized (LazyMan.class){
                if(lazyMan==null){
                    lazyMan = new SafeLazyMan(); // 有可能指令重排
                    /**
                     * 分配內存空間
                     * 執行構造方法 初始化對象
                     * 把這個對象指向這個空間
                     */
                }
            }
        }
        return lazyMan;
    }

    //原本的單例模式在反射的作用下不再單例
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        LazyMan lazyMan1 = LazyMan.getInstance();
//        LazyMan lazyMan2 = LazyMan.getInstance();
//        System.out.println(lazyMan1.hashCode()); //116211441
//        System.out.println(lazyMan2.hashCode()); //116211441
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        LazyMan lazyMan3 = declaredConstructor.newInstance();
        System.out.println(lazyMan3.hashCode()); //1625635731
        LazyMan lazyMan4 = declaredConstructor.newInstance();
    }
}
/**
 * @Description: 靜態內部類
 * @Author: wangyinghao_sx
 * @Date: 2020-05-26 13:58
 **/
public class Holder {
    private Holder() {
    }

    public static Holder getInstance() {
        return InnerClass.HOLDER;
    }

    public static class InnerClass {
        private static final Holder HOLDER = new Holder();
    }
}
/**
 * @Description: enum是什麼 本身也是一個Class 枚舉本身就是單例模式
 * @Author: wangyinghao_sx
 * @Date: 2020-05-26 15:03
 **/
public enum EnumSingle {
    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }
}

class Test{

    public static void main(String[] args) {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;
    }
}

深入瞭解CAS

什麼是CAS
比較當前工作內存中的值和主內存的值,如果這個值是期望的,那麼執行操作,如果不是就一直循環

public class CASDemo {

    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2000);

        //public final boolean compareAndSet(int expect, int update)
        //期望 更新
        //如果期望的值達到了 就更新 否則不更新 CAS是CPU的併發原語
        atomicInteger.compareAndSet(2000,2021);
        System.out.println(atomicInteger.get());
    }
}

自旋鎖
在這裏插入圖片描述
缺點:

  1. 循環會耗時(do while)
  2. 一次性只能保證一個共享變量的原子性
  3. ABA問題
    Unsafe類—>自旋鎖

ABA問題:
引入原子引用

new AtomicStampedReference(100,1);

在這裏插入圖片描述

各種鎖的理解

1.公平鎖 非公平鎖
公平鎖:非常公平 先來後到
非公平鎖:可以插隊 默認非公平鎖

    public ReentrantLock() {
        sync = new NonfairSync();
    }

2.可重入鎖
可重入鎖(遞歸鎖)
在這裏插入圖片描述

/**
 * @Description: A執行完sms發完短信之後 正常來說在進入call的時候應該釋放並重新獲取鎖 但由於可重入鎖 並沒有重新獲取鎖 所以B永遠都是在A執行完sms和call之後執行
 * @Author: wangyinghao_sx
 * @Date: 2020-05-26 17:12
 **/
public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(()->{
            phone.sms();
        },"A").start();

        new Thread(()->{
            phone.sms();
        },"B").start();
    }
}

class Phone{
    public synchronized void sms(){
        System.out.println(Thread.currentThread().getName()+"sms");
        call();
    }

    public synchronized void call(){
        System.out.println(Thread.currentThread().getName()+"call");
    }
}
/**
 * @Description:
 * @Author: wangyinghao_sx
 * @Date: 2020-05-26 17:12
 **/
public class Demo02 {
    public static void main(String[] args) {
        Phone1 phone = new Phone1();

        new Thread(()->{
            phone.sms();
        },"A").start();

        new Thread(()->{
            phone.sms();
        },"B").start();
    }
}

class Phone1{
    Lock lock = new ReentrantLock();
    public void sms(){
        lock.lock();  //細節問題:sms的兩個鎖配對 lock1->lock2->lock2.unlock->lock1.unlock
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"sms");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            lock.unlock();//鎖數目需要配對
        }
        call();
    }

    public void call(){

        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"call");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

3.自旋鎖
spinlock

public class SpinlockDemo {

    AtomicReference atomicReference = new AtomicReference();

    // 加鎖
    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName());
        while (!atomicReference.compareAndSet(null, thread)) {

        }
    }

    // 解鎖
    public void myUnLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName());
        atomicReference.compareAndSet(thread, null);
    }
}
public class TestSpinLock {
    public static void main(String[] args) throws InterruptedException {

        //底層使用自旋鎖CAS
        SpinlockDemo spinlockDemo = new SpinlockDemo();
        new Thread(()->{
            spinlockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                spinlockDemo.myUnLock();
            }
        },"T1").start();

        TimeUnit.SECONDS.sleep(1);
        //T2必須等待T1解鎖之後才能解鎖
        new Thread(()->{
            spinlockDemo.myLock();
            try {
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                spinlockDemo.myUnLock();
            }
        },"T2").start();

    }
}

死鎖解決

  1. 使用jps -l定位進程號
  2. 使用jstack +進程號 查看進程信息
Java stack information for the threads listed above:
===================================================
"Thread-1":
        at com.wang.lock.MyThread.run(Test.java:39)
        - waiting to lock <0x000000076ac27580> (a java.lang.String)
        - locked <0x000000076ac275b0> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)
"Thread-0":
        at com.wang.lock.MyThread.run(Test.java:39)
        - waiting to lock <0x000000076ac275b0> (a java.lang.String)
        - locked <0x000000076ac27580> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.
```
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章