1. 程式人生 > >java高併發系列 - 第14天:JUC中的LockSupport工具類,必備技能

java高併發系列 - 第14天:JUC中的LockSupport工具類,必備技能

這是java高併發系列第14篇文章。

本文主要內容:

  1. 講解3種讓執行緒等待和喚醒的方法,每種方法配合具體的示例
  2. 介紹LockSupport主要用法
  3. 對比3種方式,瞭解他們之間的區別

LockSupport位於java.util.concurrent(簡稱juc)包中,算是juc中一個基礎類,juc中很多地方都會使用LockSupport,非常重要,希望大家一定要掌握。

關於執行緒等待/喚醒的方法,前面的文章中我們已經講過2種了:

  1. 方式1:使用Object中的wait()方法讓執行緒等待,使用Object中的notify()方法喚醒執行緒
  2. 方式2:使用juc包中Condition的await()方法讓執行緒等待,使用signal()方法喚醒執行緒

這2種方式,我們先來看一下示例。

使用Object類中的方法實現執行緒等待和喚醒

示例1:

package com.itsoku.chat10;

import java.util.concurrent.TimeUnit;

/**
 * 微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
public class Demo1 {

    static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " start!");
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " 被喚醒!");
            }
        });
        t1.setName("t1");
        t1.start();
        //休眠5秒
        TimeUnit.SECONDS.sleep(5);
        synchronized (lock) {
            lock.notify();
        }
    }
}

輸出:

1563592938744,t1 start!
1563592943745,t1 被喚醒!

t1執行緒中呼叫lock.wait()方法讓t1執行緒等待,主執行緒中休眠5秒之後,呼叫lock.notify()方法喚醒了t1執行緒,輸出的結果中,兩行結果相差5秒左右,程式正常退出。

示例2

我們把上面程式碼中main方法內部改一下,刪除了synchronized關鍵字,看看有什麼效果:

package com.itsoku.chat10;

import java.util.concurrent.TimeUnit;

/**
 * 微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
public class Demo2 {

    static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " start!");
            try {
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " 被喚醒!");
        });
        t1.setName("t1");
        t1.start();
        //休眠5秒
        TimeUnit.SECONDS.sleep(5);
        lock.notify();
    }
}

執行結果:

Exception in thread "t1" java.lang.IllegalMonitorStateException
1563593178811,t1 start!
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Object.java:502)
    at com.itsoku.chat10.Demo2.lambda$main$0(Demo2.java:16)
    at java.lang.Thread.run(Thread.java:745)
Exception in thread "main" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at com.itsoku.chat10.Demo2.main(Demo2.java:26)

上面程式碼中將synchronized去掉了,發現呼叫wait()方法和呼叫notify()方法都丟擲了IllegalMonitorStateException異常,原因:Object類中的wait、notify、notifyAll用於執行緒等待和喚醒的方法,都必須在同步程式碼中執行(必須用到關鍵字synchronized)。

示例3

喚醒方法在等待方法之前執行,執行緒能夠被喚醒麼?程式碼如下:

package com.itsoku.chat10;

import java.util.concurrent.TimeUnit;

/**
 * 微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
public class Demo3 {

    static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lock) {
                System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " start!");
                try {
                    //休眠3秒
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " 被喚醒!");
            }
        });
        t1.setName("t1");
        t1.start();
        //休眠1秒之後喚醒lock物件上等待的執行緒
        TimeUnit.SECONDS.sleep(1);
        synchronized (lock) {
            lock.notify();
        }
        System.out.println("lock.notify()執行完畢");
    }
}

執行程式碼,輸出結果:

lock.notify()執行完畢
1563593869797,t1 start!

輸出了上面2行之後,程式一直無法結束,t1執行緒呼叫wait()方法之後無法被喚醒了,從輸出中可見,notify()方法在wait()方法之前執行了,等待的執行緒無法被喚醒了。說明:喚醒方法在等待方法之前執行,執行緒無法被喚醒。

關於Object類中的使用者執行緒等待和喚醒的方法,總結一下:

  1. wait()/notify()/notifyAll()方法都必須放在同步程式碼(必須在synchronized內部執行)中執行,需要先獲取鎖
  2. 執行緒喚醒的方法(notify、notifyAll)需要在等待的方法(wait)之後執行,等待中的執行緒才可能會被喚醒,否則無法喚醒

使用Condition實現執行緒的等待和喚醒

Condition的使用,前面的文章講過,對這塊不熟悉的可以移步JUC中Condition的使用,關於Condition我們準備了3個示例。

示例1

package com.itsoku.chat10;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
public class Demo4 {

    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            lock.lock();
            try {
                System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " start!");
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " 被喚醒!");
            } finally {
                lock.unlock();
            }
        });
        t1.setName("t1");
        t1.start();
        //休眠5秒
        TimeUnit.SECONDS.sleep(5);
        lock.lock();
        try {
            condition.signal();
        } finally {
            lock.unlock();
        }

    }
}

輸出:

1563594349632,t1 start!
1563594354634,t1 被喚醒!

t1執行緒啟動之後呼叫condition.await()方法將執行緒處於等待中,主執行緒休眠5秒之後呼叫condition.signal()方法將t1執行緒喚醒成功,輸出結果中2個時間戳相差5秒。

示例2

我們將上面程式碼中的lock.lock()、lock.unlock()去掉,看看會發生什麼。程式碼:

package com.itsoku.chat10;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
public class Demo5 {

    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " start!");
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " 被喚醒!");
        });
        t1.setName("t1");
        t1.start();
        //休眠5秒
        TimeUnit.SECONDS.sleep(5);
        condition.signal();
    }
}

輸出:

Exception in thread "t1" java.lang.IllegalMonitorStateException
1563594654865,t1 start!
    at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1723)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2036)
    at com.itsoku.chat10.Demo5.lambda$main$0(Demo5.java:19)
    at java.lang.Thread.run(Thread.java:745)
Exception in thread "main" java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1939)
    at com.itsoku.chat10.Demo5.main(Demo5.java:29)

有異常發生,condition.await();condition.signal();都觸發了IllegalMonitorStateException異常。原因:呼叫condition中執行緒等待和喚醒的方法的前提是必須要先獲取lock的鎖。

示例3

喚醒程式碼在等待之前執行,執行緒能夠被喚醒麼?程式碼如下:

package com.itsoku.chat10;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
public class Demo6 {

    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.lock();
            try {
                System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " start!");
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " 被喚醒!");
            } finally {
                lock.unlock();
            }
        });
        t1.setName("t1");
        t1.start();
        //休眠5秒
        TimeUnit.SECONDS.sleep(1);
        lock.lock();
        try {
            condition.signal();
        } finally {
            lock.unlock();
        }
        System.out.println(System.currentTimeMillis() + ",condition.signal();執行完畢");
    }
}

執行結果:

1563594886532,condition.signal();執行完畢
1563594890532,t1 start!

輸出上面2行之後,程式無法結束,程式碼結合輸出可以看出signal()方法在await()方法之前執行的,最終t1執行緒無法被喚醒,導致程式無法結束。

關於Condition中方法使用總結:

  1. 使用Condtion中的執行緒等待和喚醒方法之前,需要先獲取鎖。否者會報IllegalMonitorStateException異常
  2. signal()方法先於await()方法之前呼叫,執行緒無法被喚醒

Object和Condition的侷限性

關於Object和Condtion中執行緒等待和喚醒的侷限性,有以下幾點:

  1. 2中方式中的讓執行緒等待和喚醒的方法能夠執行的先決條件是:執行緒需要先獲取鎖
  2. 喚醒方法需要在等待方法之後呼叫,執行緒才能夠被喚醒

關於這2點,LockSupport都不需要,就能實現執行緒的等待和喚醒。下面我們來說一下LockSupport類。

LockSupport類介紹

LockSupport類可以阻塞當前執行緒以及喚醒指定被阻塞的執行緒。主要是通過park()和unpark(thread)方法來實現阻塞和喚醒執行緒的操作的。

每個執行緒都有一個許可(permit),permit只有兩個值1和0,預設是0。

  1. 當呼叫unpark(thread)方法,就會將thread執行緒的許可permit設定成1(注意多次呼叫unpark方法,不會累加,permit值還是1)。
  2. 當呼叫park()方法,如果當前執行緒的permit是1,那麼將permit設定為0,並立即返回。如果當前執行緒的permit是0,那麼當前執行緒就會阻塞,直到別的執行緒將當前執行緒的permit設定為1時,park方法會被喚醒,然後會將permit再次設定為0,並返回。

注意:因為permit預設是0,所以一開始呼叫park()方法,執行緒必定會被阻塞。呼叫unpark(thread)方法後,會自動喚醒thread執行緒,即park方法立即返回。

LockSupport中常用的方法

阻塞執行緒

  • void park():阻塞當前執行緒,如果呼叫unpark方法或者當前執行緒被中斷,從能從park()方法中返回

  • void park(Object blocker):功能同方法1,入參增加一個Object物件,用來記錄導致執行緒阻塞的阻塞物件,方便進行問題排查

  • void parkNanos(long nanos):阻塞當前執行緒,最長不超過nanos納秒,增加了超時返回的特性

  • void parkNanos(Object blocker, long nanos):功能同方法3,入參增加一個Object物件,用來記錄導致執行緒阻塞的阻塞物件,方便進行問題排查

  • void parkUntil(long deadline):阻塞當前執行緒,直到deadline,deadline是一個絕對時間,表示某個時間的毫秒格式

  • void parkUntil(Object blocker, long deadline):功能同方法5,入參增加一個Object物件,用來記錄導致執行緒阻塞的阻塞物件,方便進行問題排查;

喚醒執行緒

  • void unpark(Thread thread):喚醒處於阻塞狀態的指定執行緒

示例1

主執行緒執行緒等待5秒之後,喚醒t1執行緒,程式碼如下:

package com.itsoku.chat10;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
public class Demo7 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " start!");
            LockSupport.park();
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " 被喚醒!");
        });
        t1.setName("t1");
        t1.start();
        //休眠5秒
        TimeUnit.SECONDS.sleep(5);
        LockSupport.unpark(t1);
        System.out.println(System.currentTimeMillis() + ",LockSupport.unpark();執行完畢");
    }
}

輸出:

1563597664321,t1 start!
1563597669323,LockSupport.unpark();執行完畢
1563597669323,t1 被喚醒!

t1中呼叫LockSupport.park();讓當前執行緒t1等待,主執行緒休眠了5秒之後,呼叫LockSupport.unpark(t1);將t1執行緒喚醒,輸出結果中1、3行結果相差5秒左右,說明t1執行緒等待5秒之後,被喚醒了。

LockSupport.park();無引數,內部直接會讓當前執行緒處於等待中;unpark方法傳遞了一個執行緒物件作為引數,表示將對應的執行緒喚醒。

示例2

喚醒方法放在等待方法之前執行,看一下執行緒是否能夠被喚醒呢?程式碼如下:

package com.itsoku.chat10;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * 微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
public class Demo8 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " start!");
            LockSupport.park();
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " 被喚醒!");
        });
        t1.setName("t1");
        t1.start();
        //休眠1秒
        TimeUnit.SECONDS.sleep(1);
        LockSupport.unpark(t1);
        System.out.println(System.currentTimeMillis() + ",LockSupport.unpark();執行完畢");
    }
}

輸出:

1563597994295,LockSupport.unpark();執行完畢
1563597998296,t1 start!
1563597998296,t1 被喚醒!

程式碼中啟動t1執行緒,t1執行緒內部休眠了5秒,然後主執行緒休眠1秒之後,呼叫了LockSupport.unpark(t1);喚醒執行緒t1,此時LockSupport.park();方法還未執行,說明喚醒方法在等待方法之前執行的;輸出結果中2、3行結果時間一樣,表示LockSupport.park();沒有阻塞了,是立即返回的。

說明:喚醒方法在等待方法之前執行,執行緒也能夠被喚醒,這點是另外2中方法無法做到的。Object和Condition中的喚醒必須在等待之後呼叫,執行緒才能被喚醒。而LockSupport中,喚醒的方法不管是在等待之前還是在等待之後呼叫,執行緒都能夠被喚醒。

示例3

park()讓執行緒等待之後,是否能夠響應執行緒中斷?程式碼如下:

package com.itsoku.chat10;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * 微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
public class Demo9 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " start!");
            System.out.println(Thread.currentThread().getName() + ",park()之前中斷標誌:" + Thread.currentThread().isInterrupted());
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + ",park()之後中斷標誌:" + Thread.currentThread().isInterrupted());
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + " 被喚醒!");
        });
        t1.setName("t1");
        t1.start();
        //休眠5秒
        TimeUnit.SECONDS.sleep(5);
        t1.interrupt();

    }
}

輸出:

1563598536736,t1 start!
t1,park()之前中斷標誌:false
t1,park()之後中斷標誌:true
1563598541736,t1 被喚醒!

t1執行緒中呼叫了park()方法讓執行緒等待,主執行緒休眠了5秒之後,呼叫t1.interrupt();給執行緒t1傳送中斷訊號,然後執行緒t1從等待中被喚醒了,輸出結果中的1、4行結果相差5秒左右,剛好是主執行緒休眠了5秒之後將t1喚醒了。結論:park方法可以相應執行緒中斷。

LockSupport.park方法讓執行緒等待之後,喚醒方式有2種:

  1. 呼叫LockSupport.unpark方法
  2. 呼叫等待執行緒的interrupt()方法,給等待的執行緒傳送中斷訊號,可以喚醒執行緒

示例4

LockSupport有幾個阻塞放有一個blocker引數,這個引數什麼意思,上一個例項程式碼,大家一看就懂了:

package com.itsoku.chat10;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * 微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
public class Demo10 {

    static class BlockerDemo {
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            LockSupport.park();
        });
        t1.setName("t1");
        t1.start();

        Thread t2 = new Thread(() -> {
            LockSupport.park(new BlockerDemo());
        });
        t2.setName("t2");
        t2.start();
    }
}

執行上面程式碼,然後用jstack檢視一下執行緒的堆疊資訊:

"t2" #13 prio=5 os_prio=0 tid=0x00000000293ea800 nid=0x91e0 waiting on condition [0x0000000029c3f000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000007180bfeb0> (a com.itsoku.chat10.Demo10$BlockerDemo)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at com.itsoku.chat10.Demo10.lambda$main$1(Demo10.java:22)
        at com.itsoku.chat10.Demo10$$Lambda$2/824909230.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:745)

"t1" #12 prio=5 os_prio=0 tid=0x00000000293ea000 nid=0x9d4 waiting on condition [0x0000000029b3f000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:304)
        at com.itsoku.chat10.Demo10.lambda$main$0(Demo10.java:16)
        at com.itsoku.chat10.Demo10$$Lambda$1/1389133897.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:745)

程式碼中,執行緒t1和t2的不同點是,t2中呼叫park方法傳入了一個BlockerDemo物件,從上面的執行緒堆疊資訊中,發現t2執行緒的堆疊資訊中多了一行- parking to wait for <0x00000007180bfeb0> (a com.itsoku.chat10.Demo10$BlockerDemo),剛好是傳入的BlockerDemo物件,park傳入的這個引數可以讓我們線上程堆疊資訊中方便排查問題,其他暫無他用。

LockSupport的其他等待方法,包含有超時時間了,過了超時時間,等待方法會自動返回,讓執行緒繼續執行,這些方法在此就不提供示例了,有興趣的朋友可以自己動動手,練一練。

執行緒等待和喚醒的3種方式做個對比

到目前為止,已經說了3種讓執行緒等待和喚醒的方法了

  1. 方式1:Object中的wait、notify、notifyAll方法
  2. 方式2:juc中Condition介面提供的await、signal、signalAll方法
  3. 方式3:juc中的LockSupport提供的park、unpark方法

3種方式對比:

Object Condtion LockSupport
前置條件 需要在synchronized中執行 需要先獲取Lock的鎖
無限等待 支援 支援 支援
超時等待 支援 支援 支援
等待到將來某個時間返回 不支援 支援 支援
等待狀態中釋放鎖 會釋放 會釋放 不會釋放
喚醒方法先於等待方法執行,能否喚醒執行緒 可以
是否能響應執行緒中斷
執行緒中斷是否會清除中斷標誌
是否支援等待狀態中不響應中斷 不支援 支援 不支援

java高併發系列

  • java高併發系列 - 第1天:必須知道的幾個概念
  • java高併發系列 - 第2天:併發級別
  • java高併發系列 - 第3天:有關並行的兩個重要定律
  • java高併發系列 - 第4天:JMM相關的一些概念
  • java高併發系列 - 第5天:深入理解程序和執行緒
  • java高併發系列 - 第6天:執行緒的基本操作
  • java高併發系列 - 第7天:volatile與Java記憶體模型
  • java高併發系列 - 第8天:執行緒組
  • java高併發系列 - 第9天:使用者執行緒和守護執行緒
  • java高併發系列 - 第10天:執行緒安全和synchronized關鍵字
  • java高併發系列 - 第11天:執行緒中斷的幾種方式
  • java高併發系列 - 第12天JUC:ReentrantLock重入鎖
  • java高併發系列 - 第13天:JUC中的Condition物件

java高併發系列連載中,總計估計會有四五十篇文章,可以關注公眾號:javacode2018,獲取最新文章。

相關推薦

java併發系列 - 14JUCLockSupport工具必備技能

這是java高併發系列第14篇文章。 本文主要內容: 講解3種讓執行緒等待和喚醒的方法,每種方法配合具體的示例 介紹LockSupport主要用法 對比3種方式,瞭解他們之間的區別 LockSupport位於java.util.concurrent(簡稱juc)包中,算是juc中一個基礎類,juc中很多地

java併發系列 - 15JUC的Semaphore最簡單的限流工具必備技能

這是java高併發系列第15篇文章 Semaphore(訊號量)為多執行緒協作提供了更為強大的控制方法,前面的文章中我們學了synchronized和重入鎖ReentrantLock,這2種鎖一次都只能允許一個執行緒訪問一個資源,而訊號量可以控制有多少個執行緒可以同時訪問特定的資源。 Semaphore常用

java併發系列 - 16JUC等待多執行緒完成的工具CountDownLatch必備技能

這是java高併發系列第16篇文章。 本篇內容 介紹CountDownLatch及使用場景 提供幾個示例介紹CountDownLatch的使用 手寫一個並行處理任務的工具類 假如有這樣一個需求,當我們需要解析一個Excel裡多個sheet的資料時,可以考慮使用多執行緒,每個執行緒解析一個sheet裡的資料

java併發系列 - 17JUC的迴圈柵欄CyclicBarrier常見的6種使用場景及程式碼示例

這是java高併發系列第17篇。 本文主要內容: 介紹CyclicBarrier 6個示例介紹CyclicBarrier的使用 對比CyclicBarrier和CountDownLatch CyclicBarrier簡介 CyclicBarrier通常稱為迴圈屏障。它和CountDownLatch很相似,

java併發系列 - 23JUC原子一篇就夠了

這是java高併發系列第23篇文章,環境:jdk1.8。 本文主要內容 JUC中的原子類介紹 介紹基本型別原子類 介紹陣列型別原子類 介紹引用型別原子類 介紹物件屬性修改相關原子類 預備知識 JUC中的原子類都是都是依靠volatile、CAS、Unsafe類配合來實現的,需要了解的請移步: volati

java併發系列 - 31獲取執行緒執行結果這6種方法你都知道?

這是java高併發系列第31篇。 環境:jdk1.8。 java高併發系列已經學了不少東西了,本篇文章,我們用前面學的知識來實現一個需求: 在一個執行緒中需要獲取其他執行緒的執行結果,能想到幾種方式?各有什麼優缺點? 結合這個需求,我們使用6種方式,來對之前學過的知識點做一個回顧,加深記憶。 方式1:Thre

java併發系列 - 21java的CAS操作java併發的基石

這是java高併發系列第21篇文章。 本文主要內容 從網站計數器實現中一步步引出CAS操作 介紹java中的CAS及CAS可能存在的問題 悲觀鎖和樂觀鎖的一些介紹及資料庫樂觀鎖的一個常見示例 使用java中的原子操作實現網站計數器功能 我們需要解決的問題 需求:我們開發了一個網站,需要對訪問量進行統計,使

java併發系列 - 22java底層工具Unsafe高手必須要了解

這是java高併發系列第22篇文章,文章基於jdk1.8環境。 本文主要內容 基本介紹 通過反射獲取Unsafe例項 Unsafe中的CAS操作 Unsafe中原子操作相關方法介紹 Unsafe中執行緒排程相關方法 park和unpark示例 Unsafe鎖示例 Unsafe中保證變數的可見性 Unsafe

java併發系列 - 24ThreadLocal、InheritableThreadLocal(通俗易懂)

java高併發系列第24篇文章。 環境:jdk1.8。 本文內容 需要解決的問題 介紹ThreadLocal 介紹InheritableThreadLocal 需要解決的問題 我們還是以解決問題的方式來引出ThreadLocal、InheritableThreadLocal,這樣印象會深刻一些。 目前

java併發系列 - 25掌握JUC的阻塞佇列

這是java高併發系列第25篇文章。 環境:jdk1.8。 本文內容 掌握Queue、BlockingQueue介面中常用的方法 介紹6中阻塞佇列,及相關場景示例 重點掌握4種常用的阻塞佇列 Queue介面 佇列是一種先進先出(FIFO)的資料結構,java中用Queue介面來表示佇列。 Queue介面中

java併發系列 - 27實戰篇介面效能成倍提升讓同事刮目相看現學現用

這是java高併發系列第27篇文章。 開發環境:jdk1.8。 案例講解 電商app都有用過吧,商品詳情頁,需要給他們提供一個介面獲取商品相關資訊: 商品基本資訊(名稱、價格、庫存、會員價格等) 商品圖片列表 商品描述資訊(描述資訊一般是由富文字編輯的大文字資訊) 資料庫中我們用了3張表儲存上面的資訊:

java併發系列 - 32併發計數器的實現方式有哪些?

這是java高併發系列第32篇文章。 java環境:jdk1.8。 本文主要內容 4種方式實現計數器功能,對比其效能 介紹LongAdder 介紹LongAccumulator 需求:一個jvm中實現一個計數器功能,需保證多執行緒情況下資料正確性。 我們來模擬50個執行緒,每個執行緒對計數器遞增100萬次

java併發系列-1:必須知道的幾個概念

java高併發系列-第1天:必須知道的幾個概念 同步(Synchronous)和非同步(Asynchronous) 同步和非同步通常來形容一次方法呼叫,同步方法呼叫一旦開始,呼叫者必須等到方法呼叫返回後,才能繼續後續的行為。非同步方法呼叫更像一個訊息傳遞,一旦開始,方法呼叫就會立即返回,呼叫者就可以繼續後續的

java併發系列 - 6:執行緒的基本操作

新建執行緒 新建執行緒很簡單。只需要使用new關鍵字建立一個執行緒物件,然後呼叫它的start()啟動執行緒即可。 Thread thread1 = new Thread1(); t1.start(); 那麼執行緒start()之後,會幹什麼呢?執行緒有個run()方法,start()會建立一個新的執行緒並讓

java併發系列 - 12JUC:ReentrantLock重入鎖

java高併發系列 - 第12天JUC:ReentrantLock重入鎖 本篇文章開始將juc中常用的一些類,估計會有十來篇。 synchronized的侷限性 synchronized是java內建的關鍵字,它提供了一種獨佔的加鎖方式。synchronized的獲取和釋放鎖由jvm實現,使用者不需要顯示的釋

java併發系列 - 29併發常見的限流方式

這是java高併發系列第29篇。 環境:jdk1.8。 本文內容 介紹常見的限流演算法 通過控制最大併發數來進行限流 通過漏桶演算法來進行限流 通過令牌桶演算法來進行限流 限流工具類RateLimiter 常見的限流的場景 秒殺活動,數量有限,訪問量巨大,為了防止系統宕機,需要做限流處理 國慶期間,一般

JVM系列14JVM引數之GC日誌配置

說到 Java 虛擬機器,不得不提的就是 Java 虛擬機器的 GC(Garbage Collection)日誌。而對於 GC 日誌,我們不僅要學會看懂,而且要學會如何設定對應的 GC 日誌引數。今天就讓我們來學習一下 Java 虛擬機器中所有與 GC 日誌有關的引數。相信掌握了這些引數之後,對於大家線上列印

Java併發程式設計(十)Java併發工具

1. 等待多執行緒完成的CountDownLatch CountDownLatch允許一個或多個執行緒等待其他執行緒完成操作。 1.1 應用場景 假如有這樣一個需求:我們需要解析一個Excel裡多個sheet的資料,此時可以考慮使用多 執行緒,每個執行緒解析一個sheet裡的資料

Java併發程式設計(八)Java併發容器和框架

1. ConcurrentHashMap 1.1 ConcurrentHashMap的優勢 在併發程式設計中使用HashMap可能導致程式死迴圈。而使用執行緒安全的HashTable效率又非 常低下,基於以上兩個原因,便有了ConcurrentHashMap的登場機會。

Java級特性 14節 解析XML文檔(2) - SAX 技術

rim ride brush books 適合 函數實現 jdk 特性 自定義類 一、SAX解析XML文檔   SAX的全稱是Simple APIs for XML,也即XML簡單應用程序接口。與DOM不同,SAX提供的訪問模式是一種順序模式,這是一種快速讀寫XML數據的