繼承Runnable 實現Synchronized 同步鎖


 

在java編程中,經常需要用到同步,而用得最多的也許是synchronized關鍵字了,下面看看這個關鍵字的用法。 因為synchronized關鍵字涉及到鎖的概念,所以先來了解一些相關的鎖知識。 java的內置鎖:每個java對象都可以用做一個實現同步的鎖,這些鎖成為內置鎖。線程進入同步代碼塊或方法的時候會自動獲得該鎖,在退出同步代碼塊或方法時會釋放該鎖。獲得內置鎖的唯一途徑就是進入這個鎖的保護的同步代碼塊或方法。 java內置鎖是一個互斥鎖,這就是意味着最多只有一個線程能夠獲得該鎖,當線程A嘗試去獲得線程B持有的內置鎖時,線程A必須等待或者阻塞,知道線程B釋放這個鎖,如果B線程不釋放這個鎖,那么A線程將永遠等待下去。 java的對象鎖和類鎖:java的對象鎖和類鎖在鎖的概念上基本上和內置鎖是一致的,但是,兩個鎖實際是有很大的區別的,對象鎖是用於對象實例方法,或者一個對象實例上的,類鎖是用於類的靜態方法或者一個類的class對象上的。我們知道,類的對象實例可以有很多個,但是每個類只有一個class對象,所以不同對象實例的對象鎖是互不干擾的,但是每個類只有一個類鎖。但是有一點必須注意的是,其實類鎖只是一個概念上的東西,並不是真實存在的,它只是用來幫助我們理解鎖定實例方法和靜態方法的區別的

 

若一個非抽象類實現一個接口,就必須重寫接口中所有的方法,所以你實現Runnable接口時,會重寫run()方法,run()方法只是把該線程編程可就緒狀態,start()方法是將該線程變為可運行狀態,好好理解下。。。

 

誤區一:synchronized關鍵字只能用在實現Runnable或者繼承了Thread類的子類的方法里面。

正解:如果有一塊代碼(或方法)可能被多個線程同時訪問,然后里面操作的數據修改操作可能因為不同線程的操作而不一致的時候,使用synchronized鎖定這塊代碼,確保同時只有一個線程訪問這個代碼塊。也就是說,關鍵字synchronized可以用在任何類的方法里面,即使該類沒有實現Runnable接口或者繼承Thread類。

 

誤區二:synchronized(this)和synchronized(object)作用范圍完全不同。

正解:當多個線程訪問同一個類 A 的方法 A() 的時候。並且,這個方法 A() ,要求一個線程執行完了之后再給另外一個線程去執行。那么,這個方法 A() 就必須加上 synchronized 關鍵字,或者,在該方法 A() 中寫上 

synchronized(this//指代當前類A的實例) { }

如果不在聲明方法 A() 時,加上 synchronized 關鍵字,或者,不在方法 A() 中加上 synchronized(this){ }
同步塊的時候可以在線程類的 run() 方法內

synchronized(Object //指代類A的實例){
 Object.A();
}
實現多線程同時有序訪問該同步塊內類 A 的方法 A() 的目的。

 

object本身就包含this的情況。

this 指代的是當前同步塊所在方法所在的類,當不需要引用別的類的時候。

object 指代的是需要調用的類,引用了別的類,且需要處理多線程並發訪問時,object 指代的是被引用的類。如果沒有引用別的類,則指代的就是同步塊所在方法所在的類本身

 

一般來說,一個方法處理的內容很多,如果synchronized修飾以后,其他同步方法就必須等待其執行完畢才可以繼續執行,如果該方法需要較長時間處理,這就明顯會降低效率,失去了多線程的意義,所以我們可以考慮將同步的范圍縮小,即從同步一個方法縮小為同步一段代碼塊,這就是同步代碼塊產生的原因。同步代碼塊的語法是:

 

synchronized (this) {}
synchronized (object) {}

synchronized(this)我稱之為this同步代碼塊,針對的是當前對象;synchronized(object)我稱之為非this同步代碼塊,針對的是object對象。

記住一點即可:不論是同步方法還是同步代碼塊,實質上都是爭奪鎖的問題,而鎖一定是對象級的,即一個對象只會產生一個鎖,所以只要有一個線程在執行synchronized修飾的東西(不論是方法還是代碼塊),那么其他線程都無法訪問被synchronized修飾的方法或代碼塊。
但是注意使用非this同步代碼塊的時候,里面的object不要用String類型的,因為大家都知道JVM具有String常量池緩存的功能,所以使用String類型可能產生問題。

 

下面用一個栗子來區分同步方法還有同步代碼塊的synchronized (this)和synchronized (object)

MethodSync 方法

例如 :

public class MethodSync{
    private String anyString;

    public MultiSyn(String anyString) {
        this.anyString = anyString;
    }

/*
* 同步類方法
* @Task : 測試 synchronized 修飾方法時鎖定的是調用該方法的對象
* @param name 線程的標記名稱
*/
public synchronized void method(String name){
System.out.println(name + " Start a sync method");
try{
Thread.sleep(300);
}catch(InterruptedException e){}
System.out.println(name + " End the sync method");
}

/**
* 同步代碼塊
* @param name
*/
public void asyncMethod(String name){
synchronized (MethodSync.class){
try {
System.out.println("同步代碼塊!!!!"+name);
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(name + " End the sync method");
}
}
}

/** * 非this同步代碼塊 */ public void synObject() { synchronized (anyString) { System.out.println(anyString + " IN synchronized (anyString)同步代碼塊"); System.out.println(anyString + " OUT synchronized (anyString)同步代碼塊"); } } }

 RunnableService 調用

public class RunnableService implements Runnable {


    /**
     *  一、
     * test1 先於 test2 執行 同步方法,但是卻后於 test2 結束。這里並沒有達到互斥的效果!
     * 原因是:MethodSync是實例變量,每次創建一個Test對象就會創建一個MethodSync對象,
     * synchronized 只會鎖定調用method()方法的那個MethodSync對象,
     * 而這里創建的兩個線程分別擁有兩個不同的MethodSync對象,它們調用method方法時就沒有互斥關系。
     *
     */

    /**
     * 二、
     * 當把Test.java 中的MethodSync 變量 用 static 來修飾時,執行結果如下:
     * 這里,正確實現了同步作用。原因如下:這里也創建了二個線程(Test 對象),
     * 但是每個Test對象共享MethodSync 變量,也即只有一個MethodSync 變量在兩個線程中執行 method方法,
     * 這樣兩個線程在執行到method 方法這段代碼時就會形成互斥
     *
     */

    private String name;

    private static MethodSync methodSync = new MethodSync();
//    private MethodSync methodSync = new MethodSync();

    public RunnableService(String name){
        this.name = name;
    }

    /**
     * run方法它本身啟動不了多線程。多線程在實現的時候看着只是重寫run方法
     * ,調用start方法啟動,其實start方法里邊還有其它操作:創建線程,調用run方法。
     */
    @Override
    public void run() {
        methodSync.method(name);
    }

    public static void main(String[] args) {
//         MethodSync methodSync = new MethodSync();
//        methodSync.method("你好1");
//        methodSync.method("你好2");
        Thread t1 = new Thread(new RunnableService("test 1"));
        Thread t2 = new Thread(new RunnableService("test 2"));
        t1.start();
        t2.start();

    }

}

 

  打印日志

test 1 Start a sync method
test 1 End the sync method
test 2 Start a sync method
test 2 End the sync method

Process finished with exit code 0

 

代碼 : https://gitee.com/xdymemory00/AsyncWithLock.git

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM