線程鎖概念

synchronized 和 volatile 使用

線程拋出異常,鎖會(huì)被釋放

如下demo

public class Demo1 {
    int count = 0;

    synchronized void m() {
        System.out.println(Thread.currentThread().getName() + ": start");
        while (true) {
            count++;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 5) {
                int i = 1 / 0;
            }
        }
    }

    public static void main(String[] args) {
        Demo1 demo1 = new Demo1();
        Runnable r = new Runnable() {
            @Override
            public void run() {
                demo1.m();
            }
        };
        new Thread(r, "t1").start();
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(r, "t2").start();
    }
}

如果不想釋放那把線程鎖,則在1 / 0 地方加上try/catch。則本段程序不會(huì)執(zhí)行線程t2代碼,因?yàn)?code>t1一直沒(méi)有釋放。

線程之間的可見(jiàn)性

主線程將值改變,子線程由于cpu的緩沖區(qū)占滿,無(wú)法被讀取主內(nèi)存的變量。

public class Demo2 {
    boolean running = true;

    synchronized void m() {
        System.out.println(": start");
        while (running) {

        }
        System.out.println(": end");
    }


    public static void main(String[] args) {
        Demo2 demo1 = new Demo2();
        new Thread(demo1::m, "t1").start();
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        demo1.running = false;
    }
}

通過(guò)定義volatile關(guān)鍵字(線程之間通信的方式)來(lái),讓緩沖區(qū)讀取主內(nèi)存數(shù)據(jù)

可見(jiàn)性

一旦某個(gè)線程修改了volatile關(guān)鍵字修飾的變量,則改變量會(huì)立即保存修改后的值到物理內(nèi)存。其他線程讀取該值時(shí),也可以立即獲取修改后的值。

在java運(yùn)行時(shí),為了提高程序運(yùn)行效率,對(duì)于一些變量的操作通常在寄存器或者CPU緩存上進(jìn)行,之后才會(huì)保存到物理內(nèi)存中,而使用volatile關(guān)鍵字修飾的變量則是直接讀取物理內(nèi)存。

volatile最佳實(shí)踐

volatile由于不能保證原子性,用戶不需要依賴于上一個(gè)變量的場(chǎng)景

Atomic使用

AtomicInteger等類,是用來(lái)解決簡(jiǎn)單的類似 count++ 原子計(jì)算的問(wèn)題。他的效率比synchronized高的多。

public class Demo2 {
    AtomicInteger count = new AtomicInteger(0);

    void m() {
        for (int i = 0; i < 100000; i++) {
            count.incrementAndGet();
        }
    }

    public static void main(String[] args) {
        Demo2 demo2 = new Demo2();
        List<Thread> threads = new ArrayList<>();
        for (int j = 0; j < 10; j++) {
            threads.add(new Thread(demo2::m, "theads" + j));
        }
        threads.forEach(o -> o.start());
        threads.forEach(o -> {
            try {
                o.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println(demo2.count.get());
    }
}

但是如果AtomicXXX多個(gè)方法不構(gòu)成原子性比如

if(count.get() < 1000) {
      count.incrementAndGet();
}

這時(shí)候在兩段代碼執(zhí)行中,可能被其他線程執(zhí)行

synchronized

  1. 不要以字符串常量作為鎖定對(duì)象
String s1="hello";
String s2="world";
synchronized(s1) {
}

鎖定的是字符串的對(duì)象,這時(shí)候鎖定S1以及S2都是鎖定的同一個(gè)對(duì)象,造成死鎖的情況。

wait/notify

  1. wait 會(huì)讓出鎖對(duì)象,notify不會(huì)釋放鎖的對(duì)象。
  2. wait 是鎖定當(dāng)前的對(duì)象,notify會(huì)隨機(jī)喚醒一個(gè)線程
CountDownLatch

每個(gè)線程執(zhí)行完一個(gè)任務(wù)“倒數(shù)”一次??偨Y(jié)來(lái)說(shuō),CountDownLatch的作用就是等待其他的線程都執(zhí)行完任務(wù),必要時(shí)可以對(duì)各個(gè)任務(wù)的執(zhí)行結(jié)果進(jìn)行匯總

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容