6

虽然我知道 Re-EntrantLocks 和 之间的理论差异synchronized,但我对以下几点感到困惑。

synchronized请参阅有关 Javarevisited 比较和Lock对象的文章中的此声明:

ReentrantLock 和 Java 中的 synchronized 关键字之间另一个值得注意的区别是,在等待 Lock 时可以中断 Thread。在 synchronized 关键字的情况下,线程可以无限期地阻塞等待锁定,并且无法控制。ReentrantLock 提供了一个名为 lockInterruptibly() 的方法,可以用来在线程等待锁时中断线程。类似地,如果锁定在特定时间段内不可用,可以使用带有超时的 tryLock() 来超时。

根据上面的陈述,我确实尝试在同步方法(即阻塞等待)上中断线程等待(),它确实抛出了一个 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() 的陈述不正确,我会感到困惑?

4

5 回答 5

4

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

重要的是要注意,您可以在技术上“中断”被阻塞的线程,因为您可以调用interrupt()它,这将设置interrupted标志。仅仅因为 aThread设置了中断标志并不意味着它不能再执行任何代码。简单地说,当它到达下一个检查中断状态的代码时,该代码可能会InterruptedException在清除标志的同时抛出一个。如果发现异常的人打算做更多的工作,那么重新设置标志或抛出相同的标志是他们(几乎是道德的)责任。

所以,是的,在您的示例中,您正在捕获在进入时引发的异常.sleep(),可能在线程被休眠之前,然后您打印证明这一点的堆栈跟踪。

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

答案是count变量增加了,你只是没有看到结果。

synchronized在 Java 中保证顺序并且可能导致饥饿,所以t2恰好最后执行,并且您在睡觉之前从未检查过计数以查看它已经3

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

中断在Lock,ReentrantLocksynchronized块上“不间断”等待的线程只会导致线程唤醒并查看是否允许它通过定义锁中的任何机制获得锁,如果它不能停放直到它再次被中断或被告知它可以锁定。当线程可以继续时,它只是继续interrupted设置它的标志。

相比之下lockInterruptibly,实际上,如果您被打断,您永远不会获得锁,而是“中止”尝试获得锁并且锁请求被取消。

lock并且lockInterruptibly可以混合使用,ReentrantLock因为锁将管理队列并跳过CANCELLEDfinally语句引起的请求,因为它们在等待锁时被中断。

总之:

  • 几乎总是可以中断一个线程。
  • 中断标志通常只在线程上由记录在抛出 时清除标志的代码清除InterruptedException,但并非所有代码都记录这一点(lockInterruptiblyonReentrantLock确实,但与AbstractQueuedSynchronizer为锁供电的不同)。
  • 中断线程有不同的行为,这取决于它当时正在做什么;
    • 一个停放的线程将被取消停放并设置它的标志,通常然后清除
    • 等待锁/同步块的线程最终将进入代码但设置了中断标志
    • get等待 lockInterruptively 或 a future 等的线程将被解除停放并按照记录的方式运行,中止获取锁。
于 2019-03-21T09:39:57.553 回答
3

synchronized是JDK 无法控制的内在锁。

同步是围绕称为内在锁或监视器锁的内部实体构建的。(API 规范通常将此实体简称为“监视器”。)内在锁在同步的两个方面都发挥作用:强制对对象状态的独占访问和建立对可见性至关重要的先发生关系。

当线程调用同步方法时,它会自动获取该方法对象的内在锁,并在方法返回时释放它。即使返回是由未捕获的异常引起的,也会发生锁定释放。

在您的示例中,您实际上是在打断sleepJDK文档提到的内容。

如果该线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法或 join()、join(long)、join(long, int) 时被阻塞, sleep(long), or sleep(long, int), 这个类的方法,那么它的中断状态会被清除并且会收到一个InterruptedException。

有关如何interrupt()工作的更多详细信息。

很多抛出InterruptedException的方法,比如sleep,被设计成取消它们当前的操作,并在收到中断时立即返回。

于 2019-05-19T10:06:58.387 回答
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();
    }
}
于 2019-03-21T12:16:02.900 回答
0

如果您删除“Thread.sleep(3000)”,您的“getCount()”方法将不会抛出异常。

您只能在睡眠中中断线程或在同步方法的情况下等待

于 2021-04-27T13:37:33.317 回答
-1

您不是在中断同步,而是在中断sleep().

于 2014-08-23T11:36:57.680 回答