当我们调用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状态。