2012-03-01 61 views
25

在Java中,如果一個同步方法包含對非同步方法的調用,另一個方法是否仍然可以訪問非同步方法與此同時?基本上我問的是,synchronized方法中的所有內容都有一個鎖(包括對其他同步方法的調用)?非常感謝如果同步方法調用另一個非同步方法,是否存在對非同步方法的鎖定

+2

對我的答案有任何反饋?請接受它,如果它幫助你。 – Gray 2014-01-10 14:07:20

+0

我認爲格雷的回答必須被接受,因爲它完全回答了問題。 – Sankalp 2015-04-07 07:00:33

回答

45

如果您在​​方法中,則鎖定其他線程也調用​​的其他方法。但是其他線程對非同步方法的調用是而不是鎖定 - 任何人都可以同時調用它們。

public synchronized void someSynchronizedMethod() { 
    ... 
    someNonSynchronizedMethod(); 
    ... 
} 

// anyone can call this method even if the someSynchronizedMethod() method has 
// been called and the lock has been locked 
public void someNonSynchronizedMethod() { 
    ... 
} 

另外,如果你打電話someSynchronizedMethod()但碰巧是someNonSynchronizedMethod()方法中,你仍然持有鎖。當您輸入一個同步塊時,該鎖被啓用,並且在退出該方法時被禁用。你可以調用各種其他的非同步方法,它們仍然會被鎖定。

但你問兩個不同的東西在你的問題:

在Java中,如果一個synchronized方法包含在同一個非同步的,可以另一種方法仍然可以訪問非同步的方法調用時間?

是的。其他方法可以訪問非同步方法。

基本上我問的是在synchronized方法中的所有東西都有一個鎖(包括對其他同步方法的調用)?

呃,是的。其他呼叫同步方法被鎖定。但是非同步方法沒有被鎖定。

此外,請記住,如果方法是static那麼鎖定位於ClassLoader中的Class對象。

// this locks on the Class object in the ClassLoader 
public static synchronized void someStaticMethod() { 

如果該方法是一個實例方法,那麼該鎖在該類的實例上。

// this locks on the instance object that contains the method 
public synchronized void someInstanceMethod() { 

在這兩種情況下有兩種不同的鎖。

最後,當你正在處理​​實例方法時,類的每個實例是什麼被鎖定的。這意味着兩個線程可以同時使用相同的​​方法與不同的實例。但是如果2個線程試圖在同一個實例上對​​方法進行操作,則會阻塞,直到另一個線程退出該方法。

+0

所以我想在同步的方法中調用非同步方法不是一個好主意......因爲可以通過其他方法同時調用它們。 – dido 2012-03-01 23:39:07

+3

如果這導致2個線程訪問需要同步的數據,那麼是的,這不是一個好主意。然而,這總是發生'toString()'和其他方法。它只取決於你的對象和用例。 – Gray 2012-03-01 23:42:40

+1

我只想在這裏拋出一個同步方法,可以同時多次調用,只要它在不同的對象上調用即可。鎖適用於特定對象 - 類的實例 - 除非方法是靜態的。我認爲很容易忘記這一點,因爲很多人(包括所有當前的海報)都會對方法進行鎖定,就好像它應用於任何對象實例一樣。 – arcy 2012-03-02 03:28:51

2

如果線程A調用同步方法M1,後者又調用非同步方法M2,則線程B仍可以調用M2而不會阻塞。

同步方法獲取並釋放調用它的對象的內部鎖。這就是爲什麼它可能會阻止。非同步方法不會嘗試獲取任何鎖(除非在代碼中明確完成)。因此,如果您需要確保M2互相排斥,則無論其呼叫者(如M1)是否同步,都應使其同步。

0

該鎖屬於線程,而不是方法(或更確切地說,它的堆棧幀)。恰恰相反,如果你有一個同步的方法,你可以保證線程在方法體啓動之前擁有鎖,並在之後釋放它。

另一個線程仍然可以調用第二個非同步方法。任何線程都可以隨時調用一個非同步方法。

2

該鎖不屬於該線程。鎖實際上屬於對象(或類級別鎖定時的類),並且一個線程在同步的上下文中獲取對象(或類中的級別鎖的情況下)的鎖。 現在,在上面討論的java中沒有鎖傳播。這裏是一個小的演示:

公共類TestThread {

/** 
* @param args 
* @throws InterruptedException 
*/ 
public static void main(String[] args) throws InterruptedException { 
    // TODO Auto-generated method stub 
    ThreadCreator1 threadCreator1 = new ThreadCreator1(); 
    ThreadCreator2 threadCreator2 = new ThreadCreator2(); 

    Thread t1 = new Thread(threadCreator1,"Thread 1"); 
    Thread t3 = new Thread(threadCreator1,"Thread 3"); 
    Thread t2 = new Thread(threadCreator2,"Thread 2"); 

    t1.start(); 
    Thread.sleep(2000); 
    t3.start(); 

} 

}

公共類ThreadCreator1實現Runnable {

private static final Task task= new Task(); 
private static final Task2 task2= new Task2(); 

@Override 

public void run() { 

    try { 

     if(Thread.currentThread().getName().equals("Thread 1")) 
      task.startTask2(task2); 
     if(Thread.currentThread().getName().equals("Thread 3")) 
      task2.startTask(); 

    } catch (InterruptedException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
    // TODO Auto-generated method stub 

    /**/ 

    } 
} 

公共類任務{

public static final Task task = new Task(); 
public static List<String> dataList = new ArrayList<String>(); 
ReentrantLock lock = new ReentrantLock(); 



public void startTask2(Task2 task2) throws InterruptedException 
{ 

    try{ 

     lock.lock(); 
     //new Task2().startTask(); 
     task2.startTask(); 
    } 
    catch(Exception e) 
    { 

    } 
    finally{ 
     lock.unlock(); 
    } 
} 

}

公共類任務2 {

ReentrantLock lock = new ReentrantLock(); 
public void startTask() throws InterruptedException 
{ 

    try{ 
     //lock.lock(); 
     for(int i =0 ;i< 10;i++) 
    { 
     System.out.println(" *** Printing i:"+i+" for:"+Thread.currentThread().getName()); 
     Thread.sleep(1000); 
    } 
    } 
    catch(Exception e) 
    { 

    } 
    /*finally 
    { 
     lock.unlock(); 
    }*/ 
} 

}

只是我用折返鎖定在這裏。 如果上面的代碼運行,那麼線程1和線程3之間會出現交錯,但是如果Task2類的鎖定部分未被註釋,那麼將不會交織,並且首先獲取鎖的線程將首先完成,那麼它將釋放鎖,然後另一個線程可以繼續。