JAVA拾遺 - 線程安全之隱式鎖

線程安全

當多個線程同時操作一個數據結構的時候,可能會發生一些奇妙的情況(比如相互串行或者相互修改),這種情況發生後就無法保證數據的一致性,這也是不安全的線程,爲了保證數據的一致性,我們在這裏討論線程安全。

1.首先我們新建一個線程

package ThreadSecure;

public class Thread_A extends Thread{
    private work work; //這是工作

    public Thread_A(work work)
    {
        this.work = work;
    }
    @Override
    public void run()
    {
        work.add();

    }

}

這是一個不包含任何線程安全寫法的線程,這在單例模式中是非常危險的。
這個線程主要實現一個功能就是運行一個“任務”,然後在任務結束後使得num獲得++,

2.然後我們編輯工作內容

package ThreadSecure;

public class work {
    public int number = 0;
    public void add()
    {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        number = number + 1 ;
        System.out.println(Thread.currentThread().getName() + "-" + number);
    }

}

3.然後我們編輯主函數,使得主函數創建三個線程,用來執行work.add()這個功能,然後觀察結果

package ThreadSecure;

public class ThreadMain {
    public static void main(String[] args)
    {
        work work = new work();
        for(int i = 0 ; i < 5 ; i++)
        {
        Thread_A p1 = new Thread_A(work);
        p1.start();
        }
        try {
            Thread.sleep(1001);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(work.number);
    }
}

4.輸出結果

//第一次
Thread-0-1
Thread-4-2
Thread-1-3
Thread-3-3
Thread-2-2
3
//第二次
Thread-4-3
Thread-0-3
Thread-3-3
Thread-2-3
Thread-1-3
3

可以看到運行幾次的結果都是不同的,能不能按照我們所想,用number累計做了多少次work.add()完全是憑藉運氣!
所以在這裏使用JAVA的先進功能隱式鎖修改代碼

//關鍵字
synchronized(object);

隱式鎖

synchronzied是JAVA的一個關鍵字,在用其進行修飾代碼塊或者方法的時候,可以保證同一時刻最多隻有一個線程執行該代碼段,從此訪問需要講究順序,憑一個先來後到。

示例1:修飾於方法上,放在public等之後、返回類型之前

public synchronized void synMethod()
{
    //method
}

示例2:修飾於代碼塊上

public int synMethod(int al){
    synchronized (this){//synchronized(Object)
    }//一次只有一個線程可以進入
    //這裏面的object可以爲一個對象、參數本身、this,不建議對非對象使用
}

規則

  1. 當兩個進程併發地訪問一個對象的隱式鎖同步代碼塊時,同一個時間內只能有一個代碼塊得到執行
  2. 當一個線程訪問object的一個隱式鎖同步代碼塊時,另外一個線程可以訪問該對象中的非鎖條件(所以不建議對非對象使用,因爲容易混淆)
  3. 尤其關鍵的是當一個線程訪問object中的synchronized(this)方法塊時,其他線程對object中的所有其他synchronized(this)同步代碼塊的訪問將會被阻塞
  4. 也就是說,當一個線程訪問被隱式鎖鎖住的對象中的某個被鎖的代碼塊,其他所有線程對這個對象中的被鎖代碼的訪問都會被堵塞
  5. 以上規則適用於其他對象鎖

1.利用隱式鎖修改代碼

瞭解了隱式鎖後我們就嘗試用隱式鎖修改代碼,以完成我們之前的目的

package ThreadSecure;

public class work {
    public int number = 0;
    public synchronized void  add()
    {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        number = number + 1 ;
        System.out.println(Thread.currentThread().getName() + "-" + number);
    }

}

可見我們在add方法上加入了一個隱式鎖

2.運行結果

Thread-0-1
Thread-1-2
2
Thread-4-3
Thread-3-4
Thread-2-5

程序走到add處得以一步一步地執行

三種隱式鎖的寫法

1.鎖方法體

class A{
    public synchronized void method()
    {
        //method
    }
}

2.鎖this

class A{
    publicvoid method()
    {
        synchronized(this){
            //method
        }   
    }
}

3.鎖小東西

class A{
    private byte [] lock = new byte[1];
    public  void method()
    {
        synchronized(lock){
        //method
        }
    }
}

三種方法的速度大約是3>2>1
因爲鎖和解鎖一個對象是需要此對象的資源,所以鎖的對象越小,那麼效率也越高,與此同時,一個線程進入對象時調用資源也是需要一定時間的,在最關鍵的時候鎖當然比把線程“關”在門外面速度更快一點。造一個一字節的byte對象最小,所以在工作中經常這樣寫。

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