JAVA多線程-Lock入門

synchronized是java中的一個關鍵字,也就是說是Java語言內置的特性。那麼爲什麼會出現Lock呢?

  在前面文章中,我們瞭解到如果一個代碼塊被synchronized修飾了,當一個線程獲取了對應的鎖,並執行該代碼塊時,其他線程便只能一直等待,等待獲取鎖的線程釋放鎖,而這裏獲取鎖的線程釋放鎖只會有兩種情況:

  1)獲取鎖的線程執行完了該代碼塊,然後線程釋放對鎖的佔有;

  2)線程執行發生異常,此時JVM會讓線程自動釋放鎖。

  那麼如果這個獲取鎖的線程由於要等待IO或者其他原因(比如調用sleep方法)被阻塞了,但是又沒有釋放鎖,其他線程便只能乾巴巴地等待,試想一下,這多麼影響程序執行效率。

  因此就需要有一種機制可以不讓等待的線程一直無期限地等待下去(比如只等待一定的時間或者能夠響應中斷),通過Lock就可以辦到。

  再舉個例子:當有多個線程讀寫文件時,讀操作和寫操作會發生衝突現象,寫操作和寫操作會發生衝突現象,但是讀操作和讀操作不會發生衝突現象。

  但是採用synchronized關鍵字來實現同步的話,就會導致一個問題:

  如果多個線程都只是進行讀操作,所以當一個線程在進行讀操作時,其他線程只能等待無法進行讀操作。

  因此就需要一種機制來使得多個線程都只是進行讀操作時,線程之間不會發生衝突,通過Lock就可以辦到。

  另外,通過Lock可以知道線程有沒有成功獲取到鎖。這個是synchronized無法辦到的。

  總結一下,也就是說Lock提供了比synchronized更多的功能。但是要注意以下幾點:

  1)Lock不是Java語言內置的,synchronized是Java語言的關鍵字,因此是內置特性。Lock是一個類,通過這個類可以實現同步訪問;

       2)Lock和synchronized有一點非常大的不同,採用synchronized不需要用戶去手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完之後,系統會自動讓線程釋放對鎖的佔用;而Lock則必須要用戶去手動釋放鎖,如果沒有主動釋放鎖,就有可能導致出現死鎖現象。

我們先看下lock的簡單用法:

先看下不加lock的賬戶代碼:

public class Account {

    public   void add(BigDecimal amount , String name) {

        System.out.println(" 開始充值,線程名: " + Thread.currentThread().getName());

        try {

            Thread.sleep(2000);

            System.out.println(" 結束充值,線程名: " + Thread.currentThread().getName());

        } catch (InterruptedException e) {

            e.printStackTrace();

        }finally {


        }

    }

}

看下充值代碼:

package com.ck.thread;



import java.math.BigDecimal;



public class CzThread extends Thread{


    private Account account;

    private String accountName;
  

    public CzThread(Account account) {

        this.account = account;

    }  

   

    @Override

    public void run() {

        account.add(new BigDecimal("100"), accountName);

    }


    public Account getAccount() {

        return account;

    }


    public void setAccount(Account account) {

        this.account = account;

    }


    public String getAccountName() {

        return accountName;

    }


    public void setAccountName(String accountName) {

        this.accountName = accountName;

    }
   

}

 

再看下測試類:

package com.ck.thread;





public class MainThread {



    public static void main(String[] args) throws InterruptedException {

        Account account = new Account();

       

        CzThread cz1 = new CzThread(account);

        cz1.setName("thread1");

        cz1.start();

       

        CzThread cz2 = new CzThread(account);

        cz2.setName("thread2");

        cz2.start();

    }

}

運行結果:

開始充值,線程名: thread2

 開始充值,線程名: thread1

 結束充值,線程名: thread1

 結束充值,線程名: thread2

 

我們通過結果可以看出來,賬戶類是不安全的,然後我們修改下賬戶類:

package com.ck.thread;



import java.math.BigDecimal;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;



public class Account {



private Lock lock = new ReentrantLock();



public   void add(BigDecimal amount , String name) {

    lock.lock();

    System.out.println(" 開始充值,線程名: " + Thread.currentThread().getName());

    try {

        Thread.sleep(2000);

        System.out.println(" 結束充值,線程名: " + Thread.currentThread().getName());

    } catch (InterruptedException e) {

        e.printStackTrace();

    }finally {

        lock.unlock();

    }

}



}

我們加上了lock鎖,然後看下運行結果:

開始充值,線程名: thread1

 結束充值,線程名: thread1

 開始充值,線程名: thread2

 結束充值,線程名: thread2

我們看到lock的第一個作用與synchronized一樣,具有保護線程安全的作用。

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