1. 程式人生 > >[JDK] Java 多執行緒 之 執行緒安全

[JDK] Java 多執行緒 之 執行緒安全

Java 多執行緒 之 執行緒安全

多執行緒併發操作時資料共享如何安全進行?

執行緒安全與共享

多執行緒操作靜態變數(非執行緒安全)

SynchronizedLockTest:

/**
 * <p>
 *  測試類
 * </p>
 *
 * @author xiachaoyang
 * @version V1.0
 * @date 2018年07月20日 18:37
 * @modificationHistory=========================邏輯或功能性重大變更記錄
 * @modify By: {修改人} 2018年07月20日
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class SynchronizedLockTest { /** * 多執行緒操作靜態變數 * 10^3個運算正常,超出會發生count != loop * n 的情況 * 多執行緒操作靜態變數需進行加鎖 */ @Test public void multipleThreadTest(){ startAllThreads(regThreads(new CountService(100),1000)); //阻塞等待執行緒列印 while(!Constants.END); } /** * 啟動所有執行緒 * @param threads */
private void startAllThreads(Set<Thread> threads) { Iterator<Thread> iterator = threads.iterator(); Thread next; while (iterator.hasNext()){ next = iterator.next(); next.start(); } } /** * 註冊多執行緒任務集合 * 集合中都是交給主執行緒的 執行緒工作管理員 進行管理的 * @param countService * @param n * @return */
private Set<Thread> regThreads(Runnable countService, int n) { int i = 0; Set<Thread> threads = new HashSet<Thread>(16); while(i++ < n){ threads.add(new Thread(countService)); } return threads; } }

CountInterface:

package com.example.chapter1.thread;

/**
 * <p>
 *
 * </p>
 *
 * @author xiachaoyang
 * @version V1.0
 * @date 2018年09月10日 13:52
 * @modificationHistory=========================邏輯或功能性重大變更記錄
 * @modify By: {修改人} 2018年09月10日
 * @modify reason: {方法名}:{原因}
 * ...
 */
public interface CountInterface {

    void count();

    void insertThread(Thread thread) throws InterruptedException;
}


CountService:

/*
 * @ProjectName: 程式設計學習
 * @Copyright:   2018 HangZhou Yiyuery Dev, Ltd. All Right Reserved.
 * @address:     http://xiazhaoyang.tech
 * @date:        2018/7/28 18:15
 * @email:       [email protected]
 * @description: 本內容僅限於程式設計技術學習使用,轉發請註明出處.
 */
package com.example.chapter1.thread;

/**
 * <p>
 *
 * </p>
 *
 * @author xiachaoyang
 * @version V1.0
 * @date 2018年07月20日 18:48
 * @modificationHistory=========================邏輯或功能性重大變更記錄
 * @modify By: {修改人} 2018年07月20日
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class CountService implements Runnable, CountInterface {

    private int loop;

    public CountService(int loop) {
        this.loop = loop;
    }

    @Override
    public void run() {
        for (int i = 0; i < loop; i++) {
            count();
        }
        System.out.println(String.format("%s >>> count is %d", Thread.currentThread().getName(), Constants.COUNT));
    }

    @Override
    public void count() {
        Constants.COUNT++;
    }

    @Override
    public void insertThread(Thread thread) throws InterruptedException {
        //Todo nothing
    }
}
Thread-276 >>> count is 99601
Thread-273 >>> count is 99701
Thread-678 >>> count is 99801
Thread-742 >>> count is 99901

多次執行multipleThreadTest列印count不一致。

利用Lock和synchronized實現執行緒安全

利用synchronized關鍵字加鎖

/**
 * 同步方法保護靜態變數(synchronized關鍵字加鎖)
 */
@Test
public void multipleThreadSyncTest(){
    startAllThreads(regThreads(new CountSyncService(100),1000));
    //阻塞等待執行緒列印
    while(!Constants.END);
}

Constants:

/*
 * @ProjectName: 程式設計學習
 * @Copyright:   2018 HangZhou Yiyuery Dev, Ltd. All Right Reserved.
 * @address:     http://xiazhaoyang.tech
 * @date:        2018/7/28 18:15
 * @email:       [email protected]
 * @description: 本內容僅限於程式設計技術學習使用,轉發請註明出處.
 */
package com.example.chapter1.thread;

import com.sun.org.apache.bcel.internal.generic.RETURN;

/**
 * <p>
 *
 * </p>
 *
 * @author xiachaoyang
 * @version V1.0
 * @date 2018年07月20日 18:49
 * @modificationHistory=========================邏輯或功能性重大變更記錄
 * @modify By: {修改人} 2018年07月20日
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class Constants {

    public static int COUNT;

    public static boolean END = false;
    /**
     * 同步方法避免多執行緒操作變數
     */
    public synchronized void syncAdd() {
        COUNT++;
    }

    /**
     * 靜態內部類式單例
     */
    private static class SingletonHolder {
        private static final Constants instance = new Constants();
    }

    /**
     * 私有化構造器
     */
    private Constants() {}

    /**
     * 單例獲取
     * @return
     */
    public static Constants getInstance() {
        return SingletonHolder.instance;
    }
}

CountSyncService:


/*
 * @ProjectName: 程式設計學習
 * @Copyright:   2018 HangZhou Yiyuery Dev, Ltd. All Right Reserved.
 * @address:     http://xiazhaoyang.tech
 * @date:        2018/7/28 18:15
 * @email:       [email protected]
 * @description: 本內容僅限於程式設計技術學習使用,轉發請註明出處.
 */
package com.example.chapter1.thread;

/**
 * <p>
 *
 * </p>
 *
 * @author xiachaoyang
 * @version V1.0
 * @date 2018年09月10日 14:08
 * @modificationHistory=========================邏輯或功能性重大變更記錄
 * @modify By: {修改人} 2018年09月10日
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class CountSyncService implements Runnable, CountInterface{

    private int loop;

    public CountSyncService(int loop) {
        this.loop = loop;
    }


    @Override
    public void count() {
        Constants.getInstance().syncAdd();
    }

    @Override
    public void insertThread(Thread thread) throws InterruptedException {
        //
    }

    @Override
    public void run() {
        for (int i = 0; i < loop; i++) {
            count();
        }
        System.out.println(String.format("%s >>> count is %d", Thread.currentThread().getName(), Constants.COUNT));
    }
}

Constants中的syncAdd方法添加了synchronized關鍵字,如此多執行緒在操作Constants.COUNT時就會先獲取鎖,沒有的話就等待,避免多執行緒同時操作資料。

Lock實現執行緒安全(ReentrantLock)

核心測試類

/**
 * 加鎖方法保護靜態變數
 */
@Test
public void multipleThreadLockTest(){
    //lock()
    startAllThreads(regThreads(new CountLockService(10,LockMethodEnum.LOCK_METHOD_TYPE_LOCK),10));
    //lockInterruptibly()
    //startPartThreads(regCapThreads(new CountLockService(1, LockMethodEnum.LOCK_METHOD_TYPE_LOCK_INTERRUPTIBLY),5));
    //tryLock()
    //startAllThreads(regThreads(new CountLockService(10,LockMethodEnum.LOCK_METHOD_TYPE_TRY_LOCK),10));
    //阻塞等待執行緒列印
    while(!Constants.END);
}
/**
 * 構造Thread子類呼叫countLockService的方法測試 中斷等待鎖的執行緒
 * @param countLockService
 * @param loop
 * @return
 */
private Set<Thread> regCapThreads(CountLockService countLockService, int loop) {
    CapThread capThread = new CapThread(countLockService);
    return regThreads(capThread,loop);
}
/**
 * 啟動後隨機中斷一部分執行緒
 * @param threads
 */
private void startPartThreads(Set<Thread> threads) {
    Iterator<Thread> iterator = threads.iterator();
    Thread next;
    while (iterator.hasNext()){
        next = iterator.next();
        next.start();
    }
    try {
        Thread.sleep(1800);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    iterator =  threads.iterator();
    while (iterator.hasNext()){
        next = iterator.next();
        if(random()){
            next.interrupt();
        }
    }
}

/**構造隨機數*/
private boolean random() {
    return Double.valueOf(Math.random() * 3).intValue() % 2 == 0;
}

CapThread:

/*
 * @ProjectName: 程式設計學習
 * @Copyright:   2018 HangZhou Yiyuery Dev, Ltd. All Right Reserved.
 * @address:     http://xiazhaoyang.tech
 * @date:        2018/7/28 18:15
 * @email:       [email protected]
 * @description: 本內容僅限於程式設計技術學習使用,轉發請註明出處.
 */
package com.example.chapter1.thread;

/**
 * <p>
 *
 * </p>
 *
 * @author xiachaoyang
 * @version V1.0
 * @date 2018年09月10日 17:17
 * @modificationHistory=========================邏輯或功能性重大變更記錄
 * @modify By: {修改人} 2018年09月10日
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class CapThread extends Thread {

    private CountInterface countInterface;

    public CapThread(CountInterface countInterface){
        this.countInterface = countInterface;
    }

    @Override
    public void run() {
        try {
            countInterface.insertThread(Thread.currentThread());
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName()+"被中斷");
        }
    }
}


LockMethodEnum:定義列舉來區分Lock的不同加鎖方式

package com.example.chapter1.thread;

/**
 * <p>
 *
 * </p>
 *
 * @author xiachaoyang
 * @version V1.0
 * @date 2018年09月10日 15:10
 * @modificationHistory=========================邏輯或功能性重大變更記錄
 * @modify By: {修改人} 2018年09月10日
 * @modify reason: {方法名}:{原因}
 * ...
 */
public enum LockMethodEnum {
    /**lock()*/
    LOCK_METHOD_TYPE_LOCK,
    /**tryLock()*/
    LOCK_METHOD_TYPE_TRY_LOCK,
    /**lockInterruptibly()*/
    LOCK_METHOD_TYPE_LOCK_INTERRUPTIBLY,
    /**tryLock(long time, TimeUnit unit)*/
    LOCK_METHOD_TYPE_LOCK_WAIT;
}
/*
 * @ProjectName: 程式設計學習
 * @Copyright:   2018 HangZhou Yiyuery Dev, Ltd. All Right Reserved.
 * @address:     http://xiazhaoyang.tech
 * @date:        2018/7/28 18:15
 * @email:       [email protected]
 * @description: 本內容僅限於程式設計技術學習使用,轉發請註明出處.
 */
package com.example.chapter1.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 *
 * </p>
 *
 * @author xiachaoyang
 * @version V1.0
 * @date 2018年09月10日 14:08
 * @modificationHistory=========================邏輯或功能性重大變更記錄
 * @modify By: {修改人} 2018年09月10日
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class CountLockService implements Runnable, CountInterface {

    private int loop;

    private Lock lock = new ReentrantLock();

    private LockMethodEnum lockMethodTypeLock;

    public CountLockService(int loop, LockMethodEnum lockMethodTypeLock) {
        this.loop = loop;
        this.lockMethodTypeLock = lockMethodTypeLock;
    }


    @Override
    public void count() {
        Constants.COUNT++;
    }

    /**
     * 中斷等待鎖的執行緒
     * @param thread
     * @throws InterruptedException
     */
    @Override
    public void insertThread(Thread thread) throws InterruptedException {
        lock.lockInterruptibly();   //注意,如果需要正確中斷等待鎖的執行緒,必須將獲取鎖放在外面,然後將InterruptedException丟擲
        try {
            System.out.println(thread.getName() + "得到了鎖");
            long startTime = System.currentTimeMillis();
            for (;;) {
                if (System.currentTimeMillis() - startTime >= 2000){
                    handleCountTask();
                    break;
                }
            }
        } finally {
            System.out.println(Thread.currentThread().getName() + "執行finally");
            lock.unlock();
            System.out.println(thread.getName() + "釋放了鎖");
        }
        //Thread-2得到了鎖
        //Thread-1被中斷
        //Thread-5被中斷
        //Thread-3被中斷
        //Thread-2 >>> get lock
        //Thread-2 >>> count is 1
        //Thread-2執行finally
        //Thread-2釋放了鎖
        //Thread-4得到了鎖
        //Thread-4 >>> get lock
        //Thread-4 >>> count is 2
        //Thread-4執行finally
        //Thread-4釋放了鎖
    }

    @Override
    public void run() {
        switch (lockMethodTypeLock) {
            case LOCK_METHOD_TYPE_LOCK:
                doLock();
                break;
            case LOCK_METHOD_TYPE_TRY_LOCK:
                doTryLock();
                break;
        }
    }


    private void doTryLock() {
        if (lock.tryLock()) {
            try {
                handleCountTask();
            } catch (Exception e) {
                //
            } finally {
                lock.unlock();
                System.out.println(String.format("%s >>> release lock", Thread.currentThread().getName()));
            }
        } else {
            System.out.println(String.format("%s >>> get lock failed!", Thread.currentThread().getName()));
        }
        //...
        //Thread-23 >>> get lock failed!
        //Thread-59 >>> get lock failed!
        //Thread-70 >>> count is 4000
        //Thread-89 >>> get lock failed!
        //Thread-5 >>> get lock failed!
        //Thread-10 >>> get lock failed!
        //...(others failed)
    }

    private void doLock() {
        lock.lock();
        handleCountTask();
        lock.unlock();
        System.out.println(String.format("%s >>> release lock", Thread.currentThread().getName()));
        //Thread-76 >>> get lock
        //Thread-76 >>> release lock
        //Thread-76 >>> count is 10000

    }

    private void handleCountTask() {
        System.out.println(String.format("%s >>> get lock", Thread.currentThread().getName()));
        for (int i = 0; i < loop; i++) {
            count();
        }
        System.out.println(String.format("%s >>> count is %d", Thread.currentThread().getName(), Constants.COUNT));
    }

}

Lock介面中的方法

  • lock()方法是平常使用得最多的一個方法,就是用來獲取鎖。如果鎖已被其他執行緒獲取,則進行等待。

  • tryLock()方法是有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他執行緒獲取),則返回false,也就說這個方法無論如何都會立即返回。在拿不到鎖時不會一直在那等待

  • tryLock(long time, TimeUnit unit)方法和tryLock()方法是類似的,只不過區別在於這個方法在拿不到鎖時會等待一定的時間,在時間期限之內如果還拿不到鎖,就返回false。如果如果一開始拿到鎖或者在等待期間內拿到了鎖,則返回true。

  • lockInterruptibly()方法比較特殊,當通過這個方法去獲取鎖時,如果執行緒正在等待獲取鎖,則這個執行緒能夠響應中斷,即中斷執行緒的等待狀態。也就使說,當兩個執行緒同時通過lock.lockInterruptibly()想獲取某個鎖時,假若此時執行緒A獲取到了鎖,而執行緒B只有在等待,那麼對執行緒B呼叫threadB.interrupt()方法能夠中斷執行緒B的等待過程。

ReadWriteLock

  • 普通讀寫鎖方式
/**
 * 一般獲取鎖的方式
 */
@Test
public void regularTest(){
    startAllThreads(regThreads(()-> {
        get(Thread.currentThread());
    },2));
    //阻塞等待執行緒列印
    while(!Constants.END);
    //Thread-1正在進行讀操作
    //Thread-1讀操作完畢
    //Thread-0正在進行讀操作
    //Thread-0正在進行讀操作
    //Thread-0正在進行讀操作
    //Thread-0讀操作完畢
}

/**
 * 同步方法獲取當前執行緒獲取鎖的操作
 * @param thread
 */
public synchronized void get(Thread thread) {
    long start = System.currentTimeMillis();
    while(System.currentTimeMillis() - start <= 1) {
        System.out.println(thread.getName()+"正在進行讀操作");
    }
    System.out.println(thread.getName()+"讀操作完畢");
}
  • ReadWriteLock讀寫鎖

private ReadWriteLock rwl = new ReentrantReadWriteLock();
/**
 * ReadWriteLock介面定義了兩個方法,ReentrantReadWriteLock為具體實現類(讀鎖和寫鎖)
 */
@Test
public 
            
           

相關推薦

java線程線程安全

發生 stack 經典 eat int create 加鎖 情況 zed 線程安全和非線程安全是多線程的經典問題,非線程安全會在多個線程對同一個對象並發訪問時發生。 註意1: 非線程安全的問題存在於實例變量中,如果是方法內部的私有變量,則不存在非線程安全問題。 實例變量是對

[JDK] Java 執行 執行安全

Java 多執行緒 之 執行緒安全 多執行緒併發操作時資料共享如何安全進行? 執行緒安全與共享 多執行緒操作靜態變數(非執行緒安全) SynchronizedLockTest: /** * <p> * 測試類 * </p>

java執行 執行協作

也是網上看的一道題目:關於假如有Thread1、Thread2、Thread3、Thread4四條執行緒分別統計C、D、E、F四個盤的大小,所有執行緒都統計完畢交給Thread5執行緒去做彙總,應當如何實現? 蒐集整理了網上朋友提供的方法,主要有: 1. 多執行緒都是Thread或

Java基礎執行執行安全-同步鎖三種形式

首先,我們通過一個案例,演示執行緒的安全問題: 電影院要賣票,我們模擬電影院的賣票過程。假設要播放的電影是 “葫蘆娃大戰奧特曼”,本次電影的座位共100個(本場電影只能賣100張票)。我們來模擬電影院的售票視窗,實現多個視窗同時賣 “終結者”這場電影票(多個視窗一起賣這100張票)需要視窗

Java基礎學習——執行執行

1.執行緒池介紹     執行緒池是一種執行緒使用模式。執行緒由於具有空閒(eg:等待返回值)和繁忙這種不同狀態,當數量過多時其建立、銷燬、排程等都會帶來開銷。執行緒池維護了多個執行緒,當分配可併發執行的任務時,它負責排程執行緒執行工作,執行完畢後執行緒不關閉而是返回執行緒池,

Java執行執行排程(二)

(一)執行緒優先順序 執行緒優先順序用1~10表示,10表示優先順序最高,預設值是5.每個優先順序對應一個Thread類的公用靜態常量。如 public static final int MIN_PRIORITY = 1; public static final int NO

java執行-執行間的通訊

一個生產者與一個消費者 使用的方法: wait():使執行緒停止並釋放鎖。 notify():叫醒執行緒。 例子 工具類 public class ValueObject { public static String value=""; }

Java筆記-執行執行控制

執行緒控制 我們已經知道了執行緒的排程,接下來我們就可以使用如下方法物件執行緒進行控制。 1.執行緒休眠 public static void sleep(long millis):讓當前執行緒處於暫停狀態,millis引數毫秒值,即暫停時間。 程式

Java筆記-執行執行死鎖問題加簡單舉例

死鎖 導致死鎖的原因 Java中死鎖最簡單的情況是,一個執行緒T1持有鎖L1並且申請獲得鎖L2,而另一個執行緒T2持有鎖L2並且申請獲得鎖L1,因為預設的鎖申請操作都是阻塞的,所以執行緒T1和T2永遠被阻塞了。導致了死鎖。 這是最容易理解也是最簡單的死

讀書筆記:java執行執行同步

閱讀的書籍:《java瘋狂講義》 關鍵詞:執行緒安全問題,同步程式碼塊,同步方法,釋放同步監視器的鎖定,同步鎖,死鎖 執行緒安全問題:當使用多個執行緒來訪問同一個資料時,會導致一些錯誤情況的發生 到底什麼是執行緒安全問題呢,先看一個經典的案例:銀行取錢的問題

Java執行執行的狀態以及之間的切換(轉)

 博主最近幾天在面試的時候,被面試官問到了Java多執行緒的幾種狀態,無疑博主已經把該忘記的都忘記了,很是尷尬,回到家中在網上找到一篇部落格,博主認真閱讀了此文章,寫的很詳細,現轉載分享給大家: Java中執行緒的狀態分為6種。     1. 初始(N

Java執行執行排程詳解

排程的概念 給定時間結點,時間間隔,任務次數,然後自動執行任務 應用場景舉例 1.日誌清理:每隔三個月,清理公司日誌檔案 2.日誌備份:每個一週,備份公司檔案 3.工資結算:每個月29號,考勤彙報,業務結算,計算工資 排程的實現方式:

Java執行執行模式Immutable模式

Immutable模式 概念:保障資料的安全性,不可變的一種模式。 舉例:java.lang.string 實現了Immutable模式。無需為類中的方法宣告為synchronized,無論被多少個執行緒訪問String物件,物件不可變,不需要考慮執行緒的互斥處

【本人禿頂程式設計師】JAVA執行執行間的通訊方式

←←←←←←←←←←←← 我都禿頂了,還不點關注! 一,介紹 本總結我對於JAVA多執行緒中執行緒之間的通訊方式的理解,主要以程式碼結合文字的方式來討論執行緒間的通訊,故摘抄了書中的一些示例程式碼。 二,執行緒間的通訊方式 ①同步 這裡講的同步是指多個執行緒通過synchro

Java執行執行返回值

                對於Java多執行緒的理解,我以前僅僅侷限於實現Runnable介面或者繼承Thread類,然後重寫run()方法,最後start()呼叫就算完事,但是一旦涉及死鎖以及對共享資源的訪問和隨時監控執行緒的狀態和執行順序等等就不行了,所以現在開始看Thinking in java的

Java執行執行協作

無意中在一篇文章中看到一個面試題,題目內容為:現有一個統計任務,需要3個執行緒完成,在這三個執行緒完成後由一個執行緒完成最後的統計報告工作,寫出大概程式碼。 其實多執行緒問題都可以從兩個角度考慮實現,一種是java5之前的同步關鍵字去實現,另一種是java5之

Java執行執行池使用

本文介紹在Java中如何使用執行緒池。閱讀本文內容,讀者需要對多執行緒及執行緒池的知識有所瞭解,對Java執行緒池的建立及Executors, ExecutorService關鍵類有所接觸。關於這些方面的知識,可以參閱作者的前篇文章。 關鍵類和方法 建立Java執行緒池所

java執行執行執行

package com.np.ota.test.executor; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executo

Java執行執行池(二)

java.util.concurrent包是jdk1.5以後使用的執行緒庫,在jdk1.5之前主要使用java.lang和java.util中的類實現 package three.day.thread; import java.util.Random; import ja

Java執行執行池深入分析

執行緒池是併發包裡面很重要的一部分,在實際情況中也是使用很多的一個重要元件。 下圖描述的是執行緒池API的一部分。廣義上的完整執行緒池可能還包括Thread/Runnable、Timer/TimerTask等部分。這裡只介紹主要的和高階的API以及架構和原理。 大