線程同步的幾種實現方案

當多個線程對同一數據進行訪問時,容易出現線程安全問題,這個時候就須要讓線程同步來保證數據的安全。線程同步就是說在兩個或兩個以上的線程訪問同一資源的時候,須要用到某種方式來保證資源在某一時刻只能被一個線程訪問java

線程同步的實現方案:安全

1、同步代碼塊:synchronized(同步監視器)併發

  一、認識同步監視器(鎖子框架

    synchronized(同步監視器){}ide

    1)必須是引用數據類型,不能是基本數據類型性能

    2)在同步代碼塊中能夠改變同步監視器對象的值,不能改變其引用this

    3)儘可能不要使用String和包裝類Integer作同步監視器,若是要使用,則必須保證代碼快啊中不對其作任何操做spa

    4)通常使用共享資源作同步器線程

    5)能夠建立一個專門的同步監視器,沒有任何含義code

    6)建議使用final來修飾同步監視器

  二、同步代碼塊的執行過程

    1)第一個線程來到同步代碼塊,發現同步監視器是open狀態,須要close,而後執行其中的代碼

    2)第一個線程執行過程當中,發生了線程切換(阻塞 就緒),第一個線程失去了CPU,可是沒有開鎖

    3)第二個線程獲取了CPU,來到同步代碼塊,發現同步監視器close狀態,沒法執行其中的代碼,第二個也進入了阻塞狀態

    4)第一個線程再次得到CPU,執行後續代碼,執行完畢釋放鎖

    5)第二個線程再次得到CPU,來到同步代碼塊發現是開鎖狀態,重複第一個線程的處理過程 

  三、下面的代碼是用同步代碼塊來實現線程同步(多個窗口實現安全售票)

 

public class TiketsTest {
    public static void main(String[] args) {
        for(int i = 0;i<5;i++){//運用循環來開啓五個線程(模擬五個售票員)
            new Thread(new TiketsRunnable(),"售票員"+(i+1)).start();//此處爲了方便直接使用匿名對象
        }       
}

public class TiketsRunnable implements  Runnable {
    private int tikets = 100;//要賣票的總數
    private Object obj = new Object();
    @Override
    public void run() {
        while (true){
          synchronized (obj) { 
              try {
                  Thread.sleep(10);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              if (tikets <= 0) {
                  break;
              }
              System.out.println(Thread.currentThread().getName() + "賣了第" + tikets-- + "票");
          }
        }
    }
}

 

2、同步方法:修飾符 synchronized 返回值類型 方法名(參數){}

  一、不要將run()定義爲同步方法

  二、同步方法的同步監視器是this

  三、同步代碼塊的效率要高於同步方法

    1)同步方法的鎖是this,一旦鎖住一個方法,就鎖住了全部的同步方法;同步代碼塊只是鎖住了使用該同步代碼塊,而沒有鎖住使用其餘監視器的代碼塊

    2)同步方法是將線程鎖在了方法的外部,而同步代碼塊將線程鎖在了代碼塊的外部,可是倒是方法的內部

  4、下面的代碼是用同步方法來實現線程同步(多個窗口實現安全售票)

public class TiketsTest {
    public static void main(String[] args) {
        for(int i = 0;i<5;i++){//運用循環來開啓五個線程(模擬五個售票員)
            new Thread(new TiketsRunnable(),"售票員"+(i+1)).start();//此處爲了方便直接使用匿名對象
        }
    }
}

public class TiketsRunnable implements  Runnable {
    private int tikets = 3;
    private Object obj = new Object();
    @Override
    public void run() {
        while (true) {
            sell();
            if (tikets <= 0) {
                break;
            }
        }
    }
    public  synchronized  void sell(){//同步方法
        if(tikets<=0){
            return;
        }
        try {
            Thread.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "賣了第" + tikets+ "票");
        tikets --;
    }
}

3、Lock鎖

  一、Lock鎖

    1)JDK1.5後新增功能,與採用synchronized想比,lock鎖可提供多種鎖方案,更靈活

    2)java.util.concurrent.lock 中的 Lock 框架是鎖定的一個抽象,它容許把鎖定的實現做爲 Java 類,而不是做爲語言的特性來實現。這就爲 Lock 的多種實現留下了空間,各類實現可能有不一樣的調度算

    法、性能特性或者鎖定語義。

    3)ReentrantLock 類實現了 Lock ,它擁有與 synchronized 相同的併發性和內存語義,  可是添加了相似鎖投票、定時鎖等候和可中斷鎖等候的一些特性。此外,它還提供了在激烈爭用狀況下更佳的

    性能。

    注意:若是同步代碼有異常,要將unlock()寫入finally語句塊中,確保關鎖 

  二、Lock和synchronized的區別

    1)Lock是顯示鎖(須要手動開鎖、關鎖,不要忘記關鎖),synchronized是隱式鎖,遇到異常自動解鎖

    2)Lock鎖只有代碼塊鎖,synchronized有代碼塊鎖和方法鎖

    3)使用Lock鎖,JVM將花費較少的時間來調度線程,性能更好。而且具備更好的擴展性(提供更多的子類) 

  三、下面的代碼是用Lock鎖來實現線程同步(多個窗口實現安全售票)

public class TiketsTest {
    public static void main(String[] args) {
        for(int i = 0;i<5;i++){//運用循環來開啓五個線程(模擬五個售票員)
            new Thread(new TiketsRunnable(),"售票員"+(i+1)).start();//此處爲了方便直接使用匿名對象
        }  
    }
}

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TiketsRunnable implements  Runnable {
    private int tikets = 100;
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            lock.lock();//開
            try {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (tikets <= 0) {
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "賣了第" + tikets-- + "票");
            }finally {
                lock.unlock();//放在finally語句塊中確保關鎖
            }
        }
    }
}

4、三種鎖的優先使用順序

  Lock鎖 —— 同步代碼塊(已經進入了方法體,分配了相應的資源)—— 同步方法(在方法體以外)

相關文章
相關標籤/搜索