在Java中使用synchronized(Thread.currentThread()){...}的目的是什么?

时间:2014-04-10 07:40:24

标签: java multithreading concurrency synchronization thread-safety

我在项目中面对以下代码:

synchronized (Thread.currentThread()){
    //some code
}

我不明白在currentThread上使用synchronized的原因。

之间有什么区别吗?
synchronized (Thread.currentThread()){
    //some code
}

只是

//some code

你能提供一个显示差异的例子吗?

更新

更详细的代码如下:

synchronized (Thread.currentThread()) {
       Thread.currentThread().wait(timeInterval);
}

看起来只是Thread.sleep(timeInterval)。这是真的吗?

4 个答案:

答案 0 :(得分:25)

考虑这个

    Thread t = new Thread() {
        public void run() { // A
            synchronized (Thread.currentThread()) {
                System.out.println("A");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                }
            }
        }
    };
    t.start();
    synchronized (t) { // B
        System.out.println("B");
        Thread.sleep(5000);
    }

块A和B不能同时运行,因此在给定的测试中,“A”或“B”输出将延迟5秒,哪一个将首先未定义

答案 1 :(得分:9)

虽然这几乎肯定是一个反模式,应该以不同的方式解决,但你的直接问题仍然需要一个答案。如果您的整个代码库永远不会在除Thread之外的任何Thread.currentThread()实例上获得锁定,那么这个锁实际上永远不会被争用。但是,如果你有其他任何地方

synchronized (someSpecificThreadInstance) { ... }

然后这样一个块将不得不与你所显示的块争用相同的锁。到达synchronized (Thread.currentThread())的线程可能必须等待其他线程放弃锁定。

答案 2 :(得分:6)

基本上synchronized块的存在和不存在没有区别。但是,我可以想到一种可能对这种用法有其他意义的情况。

synchronized块有一个有趣的副作用,导致在进入和离开块之前运行时创建内存障碍。内存屏障是对CPU的一种特殊指令,它强制执行多个线程之间共享的所有变量以返回其最新值。通常,线程使用其自己的共享变量的副本,并且其值仅对此线程可见。内存屏障指示线程以某种方式更新值,以便其他线程可以看到更改。

因此,在这种情况下,同步块不会执行任何锁定(因为没有真正的锁定和等待情况,至少我无法想到)(除非使用 - this answer中提到的案例已得到解决),但它强制执行共享字段的值以返回其最新值。但是,如果使用有问题的变量的代码的其他位置也使用内存屏障(比如在更新/重新分配操作周围具有相同的synchronized块),则这是正确的。尽管如此,这仍然是不是解决方案以避免竞争条件。

如果您有兴趣,我建议您阅读此article。它是关于C#和.NET框架中的内存障碍和锁定,但Java和JVM的问题类似(除了volatile字段的行为)。它帮助我理解线程,易失性字段和锁一般如何工作。

必须考虑到这种方法中的一些严肃考虑因素,这些问题在本答案的下方评论中提到过。

  • 内存屏障并不意味着锁定。访问权限仍然是非同步,并且可能会遇到竞争条件和其他潜在问题。唯一的好处是线程能够读取共享内存字段的最新值,没有使用锁。如果工作线程只从读取值并且它只关心它们是最现有的那些,同时避免锁的开销 - 一些用例可能是高性能的,一些实践使用类似的方法同步数据处理算法。
  • 上述方法不可靠。根据{{​​3}},编译器可以在优化时消除锁定语句,因为它可能认为它们是不必要的。这也将消除内存障碍。然后代码不会发出锁,如果要使用锁,或者目的是创建内存屏障,它将无法按预期工作。
  • 上面的方法也是不可靠的,因为运行时JVM 可以删除同步,因为它可以证明监视器永远不会被另一个线程获取,这对于这个构造是正确的如果代码永远不会在另一个不是当前线程的线程对象的线程对象上同步。因此,即使它在系统A上的测试期间有效,它也可能在系统B上的另一个JVM下失败。更糟糕的是,代码可能会工作一段时间,然后在应用优化时停止工作。
  • 现在代码的意图是模棱两可的,因此应该使用更明确和富有表现力的方法来实现其效果(请参阅Holger's comment以供参考)。

答案 3 :(得分:1)

您正在实施递归互斥锁

即。同一个线程可以进入同步块,但不能进入其他线程。