當我們調用lock.lock()或嘗試輸入synchronized塊時,如果其他某個線程已經獲得了該鎖,則我們的線程將阻塞。 現在我的問題是,當我們查看lock.lock()的實現時,它會將獲取鎖委托給AQS,而AQS實際將當前線程駐留在該線程中(因此調度程序無法對其進行進一步調度)。
synchronized阻塞是否也是如此?
我什至認為我的線程狀態也不同。 例如,如果我的線程在synchronized塊上被阻塞,則它將是BLOCKING,而如果我調用了
lock.lock(),則為WAITING。 我對嗎?
我關心的是以下兩種鎖定策略在Thread.status方面與通過停車而不是忙于等待來提高性能之間的區別
ReentrantLock.lock();
synchronize { /*some code */ }
抱歉,我從自動翻譯器回復。 我認為您應該附加示例代碼,這將幫助我們幫助您分析問題。 一般在使用同步時,可以不必鎖定。 鎖 ()。 一般來說,足夠同步。
調用lock或lockInterruptibly會將線程置于WAITING狀態:
Thread state for a waiting thread. A thread is in the waiting state due to calling one of the following methods:
Object.wait with no timeout
Thread.join with no timeout
LockSupport.park
以下代碼啟動四個線程,前兩個(A,B)運行相同的代碼,并通過lock方法鎖定某些監視器。 另外兩個(C,D)也運行相同的代碼,但是它們通過lockInterruptibly方法鎖定了另一個監視器:
public static synchronized void dumpThreadState(List threads) {
System.out.println("thread state dump start");
for (Thread t: threads) {
System.out.println(t.getName()+""+t.getState());
}
System.out.println("thread state dump end\
");
}
public static void main(String[] args) throws InterruptedException {
final Lock lock = new ReentrantLock();
final Lock anotherLock = new ReentrantLock();
List threads = new LinkedList();
Runnable first = new Runnable() {
@Override
public void run() {
try {
lock.lock();
}
catch (Exception ex) {
System.out.println(Thread.currentThread().getName()+" processing exception"+ex.getClass().getSimpleName());
}
while (true);
}
} ;
Runnable second = new Runnable() {
@Override
public void run() {
try {
anotherLock.lockInterruptibly();
}
catch (InterruptedException ex) {
System.out.println(Thread.currentThread().getName()+" was interrupted");
}
while (true);
}
};
threads.add(new Thread(first,"A"));
threads.add(new Thread(first,"B"));
threads.add(new Thread(second,"C"));
threads.add(new Thread(second,"D"));
dumpThreadState(threads);
for (Thread t: threads) {
t.start();
}
Thread.currentThread().sleep(100);
dumpThreadState(threads);
System.out.println("interrupting" + threads.get(1).getName());
threads.get(1).interrupt();
dumpThreadState(threads);
System.out.println("interrupting" + threads.get(3).getName());
threads.get(3).interrupt();
Thread.currentThread().sleep(100);
dumpThreadState(threads);
for (Thread t: threads) {
t.join();
}
}
它輸出:
thread state dump start
A NEW
B NEW
C NEW
D NEW
thread state dump end
thread state dump start
A RUNNABLE
B WAITING
C RUNNABLE
D WAITING
thread state dump end
interrupting B
thread state dump start
A RUNNABLE
B WAITING
C RUNNABLE
D WAITING
thread state dump end
interrupting D
D was interrupted
thread state dump start
A RUNNABLE
B WAITING
C RUNNABLE
D RUNNABLE
thread state dump end
可以看出,通過lock方法鎖定的線程不能被中斷,而通過lockInterruptibly鎖定的線程可以被中斷。
在另一個示例中,啟動了三個線程,前兩個(A,B)運行相同的代碼,并通過synchronized塊鎖定在同一監視器上。 第三個線程鎖定在另一個監視器上,但通過wait方法等待:
public static void main(String[] args) throws InterruptedException {
final Object lock = new Object();
final Object anotherLock = new Object();
List threads = new LinkedList();
Runnable first = new Runnable() {
@Override
public void run() {
synchronized(lock) {
while (true);
}
}
} ;
Runnable second = new Runnable() {
@Override
public void run() {
synchronized(anotherLock) {
try {
anotherLock.wait();
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
};
threads.add(new Thread(first,"A"));
threads.add(new Thread(first,"B"));
threads.add(new Thread(second,"C"));
dumpThreadState(threads);
for (Thread t: threads) {
t.start();
}
Thread.currentThread().sleep(100);
dumpThreadState(threads);
for (Thread t: threads) {
t.join();
}
}
它輸出:
thread state dump start
A NEW
B NEW
C NEW
thread state dump end
thread state dump start
A RUNNABLE
B BLOCKED
C WAITING
thread state dump end
線程C以WAITING狀態結束,而線程B以BLOCKING狀態結束:
Thread state for a thread blocked waiting for a monitor lock. A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method or reenter a synchronized block/method after calling Object.wait.
編輯:
這是一個非常好的線程狀態的UML圖。
阻止-在資源上被阻止,無法被中斷
等待-在資源上被阻止,但可以被中斷,通知或取消停放。
如您所見,Waiting更好地控制了另一個處理程序。例如如果兩個線程死鎖,則可以通過中斷來中斷lock()。使用同步的兩個線程時,您將陷入困境。
同步vs鎖定的行為非常相似,確切的細節在主要版本之間有所不同。
我的建議是使用
同步以獲取需要線程安全但鎖爭用非常低的簡單代碼。
在已確定有鎖爭用的地方使用Lock,或者需要其他功能,例如tryLock。
如果你這樣做
final Lock lock = new ReentrantLock();
lock.lock();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(100);
System.out.println(t +" is" + t.getState());
lock.unlock();
版畫
Thread[Thread-0,5,main] is WAITING
Thread.State狀態
等待線程的線程狀態。由于調用以下方法之一,線程處于等待狀態:
Object.wait沒有超時
Thread.join沒有超時
LockSupport.park
處于等待狀態的線程正在等待另一個線程執行特定操作。例如,一個在對象上調用Object.wait()的線程正在等待另一個線程在該對象上調用Object.notify()或Object.notifyAll()。名為Thread.join()的線程正在等待指定的線程終止。
謝謝@Peter Lawrey,當線程被lock.lock()阻塞時,線程的狀態如何?
好的一點是,lock()將被阻塞,而lockInterruptably()將被等待,因為它可以被中斷。
@PeterLawrey調用鎖也會使線程也處于等待狀態
駐留線程和同步阻塞是非常不同的。嘗試輸入同步塊時,您將明確嘗試在對象實例上獲取監視器。如果無法獲取監視器,則線程將進入"阻塞"狀態,直到監視器可用為止。停放與Object.wait()方法更相似,因為代碼知道在其他條件變為真之前它不能繼續。阻止在這里是沒有意義的,因為這將是徒勞的,因為我目前繼續的條件是正確的。此時,我進入WAITING或TIMED_WAITING狀態(取決于等待的發出方式),直到被通知(通過類似notify(),notifyAll()或unpark()的通知)為止。一旦我的條件變為真,我就進入等待狀態,然后可能會嘗試獲取監視器并在需要時進入阻塞狀態。如果我得到顯示器,則進入"運行"并繼續快樂地前進
因此,等待實際上就是知道我無法做某事,并在認為可以的情況下讓其他線程通知我。我醒來后可能導致阻塞。阻塞只是爭奪對監視器的訪問,而沒有其他明確的先決條件。
在Lock實例上調用lock()時,調用線程實際上處于等待狀態,并且沒有阻塞。這樣做的好處是可以中斷此等待狀態,這有助于避免死鎖。對于類似Lock類的東西,您可以通過tryLock(),tryLock(long,TimeUnit),lock()和lockInterruptibly()來選擇所需的等待行為。您可以指定諸如要等待多長時間以及是否可以通過調用哪種方法打擾您之類的事情。使用synchronized代碼,您沒有此類選項。您正在阻塞,并且被阻塞阻塞,直到某個線程放棄了您想要的監視器為止,如果從未中斷,您將陷入僵局。這就是為什么從Java 5和concurrent包開始,您應該避免使用synchronized關鍵字,而是嘗試使用Lock和Condition之類的東西實現類似的語義。
謝謝@ cmbaxter,實際上我想問的是,當它調用Reentrantlock.lock()時,線程實際上進入了停車狀態。 這就是為什么我的問題在ReentrantLock.lock()中被阻止的原因實際上并未被阻止
是的,如果鎖當前不可用,則調用lock會將調用線程置于WAITING狀態。