java 線程安全--Synchronized

在Java多線程編程當(dāng)中,提供了以下幾種方式來實(shí)現(xiàn)線程安全:

Synchronized--阻塞

內(nèi)部鎖(Synchronized)和顯式鎖(Lock):屬于互斥同步方法,是重量級(jí)的多線程同步機(jī)制,可能會(huì)引起上下文切換和線程調(diào)度,它同時(shí)提供內(nèi)存可見性、有序性和原子性。

Volatile--非阻塞

volatile:輕量級(jí)多線程同步機(jī)制,不會(huì)引起上下文切換和線程調(diào)度。僅提供內(nèi)存可見性、有序性保證,不提供原子性。

CAS--非阻塞

CAS原子指令:屬于非阻塞同步方法,輕量級(jí)多線程同步機(jī)制,不會(huì)引起上下文切換和線程調(diào)度。它同時(shí)提供內(nèi)存可見性、有序性和原子化更新保證。單個(gè)變量的更新就可以用原子操作

1.Synchronized, 在方法上添加 Synchronized 修飾

還是小華,小明,小航,要到淘寶最全世界最酷炫的LV 小褲褲,但是現(xiàn)在存貨只有兩條了,自然就是手快有,手慢無啦

    public static void main(String args[]){
        Order order=new Order();
        //啟動(dòng)3個(gè)線程,代表他們進(jìn)行下單
        Thread thread1=new Thread(order,"小華");
        Thread thread2=new Thread(order,"小航");
        Thread thread3=new Thread(order,"小明");
        thread1.start();
        thread2.start();
        thread3.start();
    }

    public static class Order implements Runnable{
        private  int   lvUnderWare=2;  //褲子的庫存只有兩條了

        @Override
        public  void run() {
                if (lvUnderWare > 0) {
                    System.out.println(Thread.currentThread().getName() + "開始下單買LV的小褲褲," + "訂單顯示還剩下:" + lvUnderWare + "條");
                    lvUnderWare--;
                    System.out.println(Thread.currentThread().getName() + "下單成功了,買到lv的小褲褲了");
                } else {
                    System.out.println(Thread.currentThread().getName()+"也要購買,但是訂單不足了");
                    try {
                        Thread.sleep(1000 * 60 );
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
        }
    }
image.png

可以看到訂單顯示0條的時(shí)候,還能下單成功?出問題了呀,因?yàn)樾『劫I的時(shí)候,小明也開始買了呀,但是小明下手慢呀,就木得了,所以這個(gè)訂單出現(xiàn)線程安全得問題了,我們加個(gè)同步方法吧

   @Override
        public synchronized void run() {
                if (lvUnderWare > 0) {
                    System.out.println(Thread.currentThread().getName() + "開始下單買LV的小褲褲," + "訂單顯示還剩下:" + lvUnderWare + "條");
                    lvUnderWare--;
                    System.out.println(Thread.currentThread().getName() + "下單成功了,買到lv的小褲褲了");
                } else {
                    System.out.println(Thread.currentThread().getName()+"也要購買,但是訂單不足了");
                    try {
                        Thread.sleep(1000 * 60 );
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
        }
image.png

可以看到這回小明,買不到 了,因?yàn)橐呀?jīng)賣完了呀

2.Synchronized 修飾代碼塊

 @Override
        public  void run() {
                if (lvUnderWare > 0) {
                    synchronized (this){
                    System.out.println(Thread.currentThread().getName() + "開始下單買LV的小褲褲," + "訂單顯示還剩下:" + lvUnderWare + "條");
                    lvUnderWare--;
                    System.out.println(Thread.currentThread().getName() + "下單成功了,買到lv的小褲褲了");
                }
            }
            else {
                System.out.println(Thread.currentThread().getName()+"也要購買,但是訂單不足了");
                try {
                    Thread.sleep(1000 * 60 );
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

代表獲取當(dāng)前對(duì)象的鎖;也可以是類中的一個(gè)屬性對(duì)象,代表獲取該屬性對(duì)象的鎖。我得理解就是order 類這個(gè)對(duì)象得鎖,得到得結(jié)果也是小明買不到了,也是線程安全得


image.png

3.修飾類

synchronized (Order.class)

@Override
        public  void run() {
            synchronized (Order.class) {
                if (lvUnderWare > 0) {
                    System.out.println(Thread.currentThread().getName() + "開始下單買LV的小褲褲," + "訂單顯示還剩下:" + lvUnderWare + "條");
                    lvUnderWare--;
                    System.out.println(Thread.currentThread().getName() + "下單成功了,買到lv的小褲褲了");
                } else {
                    System.out.println(Thread.currentThread().getName() + "也要購買,但是訂單不足了");
                    try {
                        Thread.sleep(1000 * 60);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

一樣得到得是線程安全的

Volicate

線程在寫入volatile變量得時(shí)候不會(huì)把值緩存在寄存器或者其他地方,而是會(huì)把值刷新回主內(nèi)存中,當(dāng)其他內(nèi)存讀取該變量得時(shí)候,會(huì)從主內(nèi)存中重新獲取該值,而不是從當(dāng)前線程的工作內(nèi)存中獲取值,實(shí)際上就是,線程寫入了volicate 變量的時(shí)候就相當(dāng)于線程退出同步的代碼塊,將變量的值寫入到主內(nèi)存中,讀取volicate變量的時(shí)候就詳單與進(jìn)入同步塊,會(huì)清空本地內(nèi)存的變量的值,然后從主內(nèi)存中獲取最新的值

 private  volatile static  int  count=1;
    public static  void main (String [] args) throws InterruptedException {
        int finalI=1;
        for(int i=1;i<=10;i++){
            new Thread(()->{
                count++;
                System.out.println(count);
            }).start();
        }
        Thread.sleep(1000);
        System.out.println("final:"+count);
    }
image.png

可以看到中間的count 的值是線程不安全的,但是最終得到的值永遠(yuǎn)都是 11
所以使用volicate 我覺得如果是要關(guān)注每個(gè)線程中間的變量值是不適合的,所以說當(dāng)寫入變量值要依賴當(dāng)前變量值,這種操作不是原子性的,而volicate不保證原子性的

鎖釋放

異常自動(dòng)釋放鎖
當(dāng)一個(gè)線程執(zhí)行的代碼出現(xiàn)異常時(shí),其所持有的鎖會(huì)自動(dòng)釋放。

最后編輯于
?著作權(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)容