可以中断同步块/方法吗?

时间:2014-08-23 11:33:15

标签: java multithreading synchronization interrupted-exception

虽然我知道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()的声明不正确,我感到很困惑?

5 个答案:

答案 0 :(得分:2)

如果没有其余的代码,则可能无法完全回答该问题。 我认为,您在这里感到困惑的是,您看到的是,虽然代码暗示您不能“中断”在synchronized锁上阻塞的线程,但是您看到的是{{1 }}变量似乎不受应该已输入此方法的线程的影响。

请注意,您可以从技术上“中断”被阻止的线程,因为可以在其上调用count,这将设置interrupt()标志。仅仅是因为interrupted设置了中断标志,不是意味着它无法执行更多代码。简而言之,当转到下一个检查中断状态的代码时,该代码可能会在清除该标志的同时抛出Thread。如果发现异常的人打算做更多的工作,那么重新设置旗帜或抛出旗帜是他们的(几乎道义上的)责任。

所以,是的,在您的示例中,您正在捕获InterruptedException在进入时引发的异常,很可能是在线程进入睡眠状态之前,然后打印了证明这一点的堆栈跟踪。

可能会引起您困惑的悬而未决的问题;那么,如果允许此代码运行到.sleep()方法调用之后,为什么我的count没有增加?

答案是.sleep()变量已增加,您只是看不到结果。

Java中的

count不能保证顺序,并且可能导致饥饿,因此synchronized恰好在最后一次执行,您从未在睡觉之前检查过计数已经是t2

因此,为回答您的问题,文档正确且行为正确。

中断正在3LockReentrantLock块上“不间断”等待的线程只会导致线程醒来,并查看是否允许其锁定,无论定义锁中采用了何种机制,如果无法锁定,它将再次停放,直到再次中断或告知可以使用该锁为止。当线程可以继续执行时,只需继续设置其synchronized标志即可。

interrupted形成对比,实际上,如果您被打扰,则永远不会获得锁,而是“中止”尝试获取锁,并且锁请求是已取消。

lockInterruptiblylock可以在同一lockInterruptibly上混合使用,因为锁将管理队列并跳过ReentrantLockCANCELLED发出的请求声明,因为它们在等待锁时被打断了。

总结:

  • 您几乎可以总是 中断线程。
  • 通常仅通过代码在线程上清除该中断标志,该代码记录了它在抛出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)

synchronizedintrinsic 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()