虽然我知道Re-EntrantLocks和synchronized
之间的理论差异,但我对以下几点感到困惑。
请参阅Javarevisited与synchronized
and Lock
objects比较的文章
还有一点值得注意的是ReentrantLock和 Java中的synchronized关键字是能够在中断线程的同时 等待锁。在synchronized关键字的情况下,线程可以 阻止等待锁定,无限期和那里 没办法控制那个。 ReentrantLock提供了一个名为的方法 lockInterruptibly(),可以用来中断线程 等待锁定。类似地,可以使用带超时的tryLock() 如果在某段时间内没有锁定,则超时。
根据上面的陈述,我确实尝试在同步方法上中断Thread waiting()(即阻塞等待)并且它确实抛出了InterruptedException。但是这种行为与上述陈述中所说的相矛盾。
// this method is called from inside run() method of every thread.
public synchronized int getCount() {
count++;
try {
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " gets " + count);
} catch (InterruptedException e) {
e.printStackTrace();
}
return count;
}
....
....
t1.start();
t2.start();
t3.start();
t4.start();
t2.interrupt();
这是我得到的输出:
Thread 1 gets 1
Thread 4 gets 2
Thread 3 gets 3
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at locks.SynchronizedLockInterrupt.getCount(SynchronizedLockInterrupt.java:10)
at locks.SynchronizedLockInterrupt$2.run(SynchronizedLockInterrupt.java:35)
at java.lang.Thread.run(Unknown Source)
如果我的示例不正确或引用的有关synchronized()的声明不正确,我感到很困惑?
答案 0 :(得分:2)
如果没有其余的代码,则可能无法完全回答该问题。
我认为,您在这里感到困惑的是,您看到的是,虽然代码暗示您不能“中断”在synchronized
锁上阻塞的线程,但是您看到的是{{1 }}变量似乎不受应该已输入此方法的线程的影响。
请注意,您可以从技术上“中断”被阻止的线程,因为可以在其上调用count
,这将设置interrupt()
标志。仅仅是因为interrupted
设置了中断标志,不是意味着它无法执行更多代码。简而言之,当转到下一个检查中断状态的代码时,该代码可能会在清除该标志的同时抛出Thread
。如果发现异常的人打算做更多的工作,那么重新设置旗帜或抛出旗帜是他们的(几乎道义上的)责任。
所以,是的,在您的示例中,您正在捕获InterruptedException
在进入时引发的异常,很可能是在线程进入睡眠状态之前,然后打印了证明这一点的堆栈跟踪。
可能会引起您困惑的悬而未决的问题;那么,如果允许此代码运行到.sleep()
方法调用之后,为什么我的count
没有增加?
答案是.sleep()
变量已增加,您只是看不到结果。
count
不能不保证顺序,并且可能导致饥饿,因此synchronized
恰好在最后一次执行,您从未在睡觉之前检查过计数已经是t2
因此,为回答您的问题,文档正确且行为正确。
中断正在3
,Lock
或ReentrantLock
块上“不间断”等待的线程只会导致线程醒来,并查看是否允许其锁定,无论定义锁中采用了何种机制,如果无法锁定,它将再次停放,直到再次中断或告知可以使用该锁为止。当线程可以继续执行时,只需继续设置其synchronized
标志即可。
与interrupted
形成对比,实际上,如果您被打扰,则永远不会获得锁,而是“中止”尝试获取锁,并且锁请求是已取消。
lockInterruptibly
和lock
可以在同一lockInterruptibly
上混合使用,因为锁将管理队列并跳过ReentrantLock
由CANCELLED
发出的请求声明,因为它们在等待锁时被打断了。
总结:
finally
时清除了该标志,但是并非所有代码都对此进行了记录(InterruptedException
上的lockInterruptibly
,但在为锁提供动力的ReentrantLock
上却不一样。AbstractQueuedSynchronizer
处于等待状态的线程将被停放并按照记录的方式运行,从而中止获取锁定。答案 1 :(得分:0)
如果添加了一个简单的例子来说明这一点。
在您的示例中,您已经获取了锁,请参见您的堆栈跟踪。 该代码是自我解释。
同步的问题在于它不是中断点,而lock.lockInterruptibly()
是中断点。请注意,lock.lock()
也不是中断点。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Foo {
public static void main(String[] args) throws InterruptedException {
// for the example with synchronized
Object monitor = new Object();
// for the example with locks
Lock lock = new ReentrantLock();
// iam lazy, just use both lock and motitor for this example
Thread one = new Thread(() -> {
lock.lock();
try {
synchronized (monitor) {
System.out.println("Thread one entered monitor");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("Thread one interrupted");
Thread.currentThread().interrupt();
}
}
} finally {
lock.unlock();
}
});
// uncomment to use the monitor object
// Thread two = new Thread(() -> {
// synchronized (monitor) {
// System.out.println("Thread two entered monitor");
// }
// });
Thread two = new Thread(() -> {
try {
lock.lockInterruptibly();
try {
System.out.println("Thread one entered lock");
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println("Thread two interrupted while waiting for lock");
Thread.currentThread().interrupt();
}
});
// start thread one
one.start();
// wait for the thread to start, too lazy to implement notifications
Thread.sleep(1000);
// start thread two
two.start();
// interrupting will wait until thread one finished
two.interrupt();
}
}
答案 2 :(得分:0)
synchronized
是intrinsic lock,这超出了JDK的控制范围。
同步是围绕内部实体(称为内部锁或监视器锁)构建的。 (API规范通常将此实体简称为“监视器”。)内在锁在同步的两个方面都起作用:强制排他访问到对象的状态并建立先于先后可见性必不可少的关系。
当线程调用同步方法时,它会自动获取该方法对象的内在锁,并在方法返回时释放它。即使返回是由未捕获的异常引起的,也会发生锁定释放。
在您的示例中,您实际上正在中断JDK doc所提到的sleep
。
如果在调用Object类的wait(),wait(long)或wait(long,int)方法或join(),join(long),join(long)时阻塞了此线程,int),sleep(long)或sleep(long,int)这类方法,则其中断状态将被清除,并且将收到InterruptedException。
More details about how interrupt()
works。
许多引发InterruptedException的方法(例如睡眠)旨在取消其当前操作,并在收到中断时立即返回。
答案 3 :(得分:0)
如果您删除“Thread.sleep(3000)”,您的“getCount()”方法将不会抛出异常。
在同步方法的情况下,您只能在睡眠或等待中中断线程
答案 4 :(得分:-1)
您不会中断同步,而是会中断sleep()
。