91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java高并發中線程不安全類與寫法是什么

發布時間:2021-10-19 15:56:27 來源:億速云 閱讀:103 作者:柒染 欄目:大數據

java高并發中線程不安全類與寫法是什么,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

什么是線程不安全類?

如果一個類的對象同時可以被多個線程訪問,如果不做特殊的同步與并發處理,就很容易表現出線程不安全的現象,比如拋出異常,比如邏輯處理錯誤等,這種類就是線程不安全類。

StringBuilder->StringBuffer

@Slf4j
public class StringExample1 {
    // 請求總數
    public static int clientTotal = 5000;

    // 同時并發執行的線程數
    public static int threadTotal = 200;

    public static StringBuilder stringBuilder = new StringBuilder();

    public static void main(String[] args) throws InterruptedException {
        //線程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //定義信號量
        final Semaphore semaphore = new Semaphore(threadTotal);
        //定義計數器
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for(int i = 0; i < clientTotal; i++) {
            executorService.execute(() ->{
                try {
                    semaphore.acquire();
                    update();
                    semaphore.release();
                } catch (InterruptedException e) {

                    log.error("exception", e);
                }
                countDownLatch.countDown();

            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("size:{}", stringBuilder.length());
    }

    public static void update() {
        stringBuilder.append("1");
    }
}

輸出結果與我們預期的不一致。StringBuilder是一個線程不安全的類。 

我們將StringBuilder換成StringBuffer,可以得到預期的效果。說明StringBuffer是線程安全的。

查看StringBuffer的append方法,發現這個方法與其他方法前添加了synchronized關鍵字。

StringBuffer因為使用了synchronized關鍵字,因此在使用的時候會有性能損耗的,因此在做字符串拼接時涉及到多線程可以考慮StringBuffer來處理。

但是很多時候,我們往往在一個方法里面做字符串拼接單獨,定義一個StringBuilder變量就可以了。因為在一個方法內部定義局部變量時屬于堆棧封閉,這時只有單個線程可以操作對象,不涉及到線程安全問題了。

SimpleDateFormat -> JodaTime 

@Slf4j
public class DateFormatExample1 {

    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
    // 請求總數
    public static int clientTotal = 5000;

    // 同時并發執行的線程數
    public static int threadTotal = 200;


    public static void main(String[] args) throws InterruptedException {
        //線程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //定義信號量
        final Semaphore semaphore = new Semaphore(threadTotal);
        //定義計數器
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for(int i = 0; i < clientTotal; i++) {
            executorService.execute(() ->{
                try {
                    semaphore.acquire();
                    update();
                    semaphore.release();
                } catch (InterruptedException e) {

                    log.error("exception", e);
                }
                countDownLatch.countDown();

            });
        }
        countDownLatch.await();
        executorService.shutdown();
    }

    public static void update() {
        try {
            simpleDateFormat.parse("20190729");
        } catch (ParseException e) {
            e.printStackTrace();
            log.error("parse Exception" + e);
        }
    }

}

運行時,會拋出異常:

Exception in thread "pool-1-thread-3" java.lang.NumberFormatException: For input string: "E.177"
	at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
	at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
	at java.lang.Double.parseDouble(Double.java:538)
	at java.text.DigitList.getDouble(DigitList.java:169)
	at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1867)
	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
	at java.text.DateFormat.parse(DateFormat.java:364)
	at com.vincent.example.commonUnsafe.DateFormatExample1.update(DateFormatExample1.java:50)
	at com.vincent.example.commonUnsafe.DateFormatExample1.lambda$main$0(DateFormatExample1.java:34)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

這是線程不安全的,simpleDateFormat不是一個線程安全的類,一種解決辦法是將SimpleDateFormat simpleDateFormat = new SimpleDateFormat()放到方法內,封閉堆棧,修改update方法如下:

public static void update() {
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
            simpleDateFormat.parse("20190729");
        } catch (ParseException e) {
            e.printStackTrace();
            log.error("parse Exception" + e);
        }
    }

JodaTime是線程安全的:

@Slf4j
@ThreadSafe
public class DateFormatExample3 {

    // 請求總數
    public static int clientTotal = 5000;

    // 同時并發執行的線程數
    public static int threadTotal = 200;

    private static DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyyMMdd");

    public static void main(String[] args) throws InterruptedException {
        //線程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //定義信號量
        final Semaphore semaphore = new Semaphore(threadTotal);
        //定義計數器
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for(int i = 0; i < clientTotal; i++) {
            final int count  = i;
            executorService.execute(() ->{
                try {
                    semaphore.acquire();
                    update(count);
                    semaphore.release();
                } catch (InterruptedException e) {

                    log.error("exception", e);
                }
                countDownLatch.countDown();

            });
        }
        countDownLatch.await();
        executorService.shutdown();
    }

    public static void update(int i) {
        log.info("{}, {}", i, DateTime.parse("20190729",dateTimeFormatter).toDate());
    }

}

ArrayList,HashSet,HashMap等Collections

通常我們使用這些集合類時他們的對象通常聲明在方法里面作為局部變量來使用,很少觸發線程不安全的問題,但是一旦定義成static的時候而且多個線程可以進行修改的時候就會容器出問題。例如下面的代碼:

@Slf4j
public class ArrayListExample {
    // 請求總數
    public static int clientTotal = 5000;

    // 同時并發執行的線程數
    public static int threadTotal = 200;

    private static List<Integer> list = new ArrayList<>();

    public static void main(String[] args) throws InterruptedException {
        //線程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //定義信號量
        final Semaphore semaphore = new Semaphore(threadTotal);
        //定義計數器
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for(int i = 0; i < clientTotal; i++) {
            final int count  = i;
            executorService.execute(() ->{
                try {
                    semaphore.acquire();
                    update(count);
                    semaphore.release();
                } catch (InterruptedException e) {

                    log.error("exception", e);
                }
                countDownLatch.countDown();

            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("size:{}",list.size()) ;
    }

    public static void update(int i) {
        list.add(i);
    }

}

輸出結果不是我們所預期的。

同樣適用HashSet,HashMap也無法輸出正確的結果。這些都是線程不安全的。

后面會介紹這些集合對應的線程安全類。

先檢查在執行 if(condition(a)) {handle(a);}

為什么這種寫法是線程不安全的?假設a是線程安全的類,即使if(condition(a))是線程安全的操作,handle(a)也是線程安全的,但是兩個結合起來就不是線程安全的了,并不是原子性的。

Atomic類在自增的時候,底層實現是通過CAS原理來保證原子性的跟新。

實際過程中,如果遇到這種情況要判斷一個對象是否滿足某個條件,然后做某個操作,一定先要考慮這個對象是否多線程共享的,如果是多線程共享的一定要在上面加鎖,或者保證操作是原子性的才可以。否則會觸發線程不安全的。

看完上述內容,你們掌握java高并發中線程不安全類與寫法是什么的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

大化| 张家川| 呼伦贝尔市| 武山县| 屏东县| 越西县| 黎川县| 延川县| 阜阳市| 大荔县| 扎兰屯市| 华阴市| 桦甸市| 中江县| 彰化县| 葫芦岛市| 阿坝县| 达州市| 永济市| 湖州市| 南溪县| 鲁山县| 湖南省| 平南县| 获嘉县| 德兴市| 宁波市| 襄垣县| 弥渡县| 荣成市| 锡林郭勒盟| 任丘市| 班戈县| 上栗县| 宁河县| 贵州省| 浦城县| 景泰县| 贺州市| 柳林县| 新宁县|