java線程---線程鎖synchronized和Lock

什麼是線程鎖

多線程可以同時執行多個任務,但是當多個線程同時訪問同一個數據時,可能導致數據不同步,甚至錯誤!
打個比方,你在銀行裏面存了一百萬,這個時候你需要從裏面取走九十萬,你女朋友也要從裏面取五十萬,如果沒有用線程鎖,那麼你們兩個人同時取錢,就有可能導致線程錯誤,你們總共從銀行取走一百四十萬元,那麼銀行就會虧本,所以要用線程鎖。

synchronized和Lock的區別

線程鎖分synchronized和Lock,那麼他們之間有什麼區別呢?

區別 synchronized Lock
鎖的釋放 自動釋放 必須使用finally中必須釋放,不然可能線程鎖死
鎖的狀態 無法判斷 可以判斷
存在層次 java的關鍵字 一個類
性能 少量同步 多量同步
範圍 不但可以鎖代碼塊,他還可以方法 只有代碼塊

首先我們先不加鎖寫一個程序

package Thread;
public class mytest03 {
    public static void main(String[] args) {
        Money money=new Money(300);
        new Thread(money,"t1").start();
        new Thread(money,"t2").start();
    }
}
class Money implements Runnable
{
    int count;
    public Money(int count) {
        this.count = count;
    }

    @Override
    public  void run() {
        while (true)
        {
                if(count<=0)
                    break;
                else
                {
                    System.out.println(Thread.currentThread().getName()+"取出了1萬元,----->還剩"+(count-1)+"萬元");
                    count=count-1;
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
        }
    }
}

看上去好像沒有什麼問題,那麼我們來看一下結果

由於結果太多,我只截屏了部分,但是大家可以看到,數據有明顯的錯誤,這個是爲什麼呢?

原來,線程是由cpu調度時間的,每個線程每次搶到的時間都是不一樣的,這個就完全看cpu的性能和自己的造化了,當線程t1搶到時間準備對count進行計算的時候,線程t2也搶到了時間,並且在這個時候,t1還未對線程進行運算。
就在這個時候,兩個線程同時對count進行了運算,那麼打個比方,count從300開始,這個時候,兩個線程就會同時輸出299,但是,經過了兩個線程的運算,每次減一,那麼count就變成了298,所以下一次就會輸出298,由於每次搶到的時間都不一樣,所以輸出的答案就不一樣,但是無非都是錯的答案,當然,如果你運氣及其逆天,說不定可以碰對,不過我是沒有碰對過,所以,我們就要應用線程鎖。

線程鎖synchronized

我們知道synchronized不僅可以鎖代碼塊還可以鎖方法,那麼我們來看一下他是怎麼鎖方法的

package Thread;
public class mytest03 {
    public static void main(String[] args) {
        Money money=new Money(300);
        new Thread(money,"t1").start();
        new Thread(money,"t2").start();
    }
}
class Money implements Runnable
{
    int count;
    public Money(int count) {
        this.count = count;
    }

    @Override
    public synchronized void run() {
        while (true)
        {
                if(count<=0)
                    break;
                else
                {
                    System.out.println(Thread.currentThread().getName()+"取出了1萬元,----->還剩"+(count-1)+"萬元");
                    count=count-1;
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
        }
    }
}

我們在上一個代碼的基礎上面給run()方法加了一個synchronized ,那麼我們得到理想的結果了嗎?我們不妨來看看

在這裏插入圖片描述
由於結果太長,我只截圖了部分,但是大家運行後會發現,全是對線程t1進行的調度,t2變成了沒人要的孤兒,這又是爲什麼呢?

原來,我們用synchronized把run()方法給鎖住了,這個時候,t1開始被調用,t1使用run方法由於裏面的while循環一直在持續,線程就是一直鎖着的,t2無法被調用,那麼就是t1在不斷的被調用直至線程結束,打個比方,a和b要去衛生間上廁所,但是a先進去了,並且把門反鎖了,這個時候b怎麼樣也進不去,除非a上完廁所自己出來。

那麼我們要怎麼寫呢?請看代碼

package Thread;
public class mytest03 {
    public static void main(String[] args) {
        Money money=new Money(300);
        new Thread(money,"t1").start();
        new Thread(money,"t2").start();
    }
}
class Money implements Runnable
{
    int count;
    public Money(int count) {
        this.count = count;
    }

    @Override
    public  void run() {
        while (true)
        {
            synchronized (this)
            {
                if(count<=0)
                    break;
                else
                {
                    System.out.println(Thread.currentThread().getName()+"取出了1萬元,----->還剩"+(count-1)+"萬元");
                    count=count-1;
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
    }
}

在這裏插入圖片描述
這一次結果就對了,這是爲什麼呢?我們可以看到,我們在while裏面給代碼塊加了一個鎖,這樣當某一個線程使用完裏面的方法後,synchronized 就會解鎖,這個時候另一個線程就可以進去了,同時線程就會鎖死,那麼這樣就是真確的了,所以在我們上鎖的時候,要考慮到是什麼使結果發生了變化,就鎖哪裏。

線程鎖Lock

package Thread;
import java.util.concurrent.locks.ReentrantLock;
public class testLock02 {
    public static void main(String[] args) {
        lock1 ll=new lock1();
        new Thread(ll,"小斌").start();
        new Thread(ll,"小龍").start();
        new Thread(ll,"小宇").start();
        new Thread(ll,"小俊").start();
    }
}

class lock1 implements  Runnable
{
     int a=600;
    private final ReentrantLock locking=new ReentrantLock();
    @Override
    public  void run() {
        while (true)
        {
            try {
                locking.lock();
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                 if(a>0)
                     System.out.println(Thread.currentThread().getName()+"獲的第"+(a--)+"張票");

            }finally {
                locking.unlock();
            }
            if(a<=0)
                break;
        }
    }
}

我們可以看到,大致和synchronized 差不多,只不過是需要用 ReentrantLock來new一個鎖,然後還要自己手動解鎖。我們要寫一個try/cath,在finally處寫“鎖名.unlock”其餘情況和synchronized 差不多,這裏大家就自行思考比較好

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章