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

gst*_*low 25 java concurrency multithreading synchronization thread-safety

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

synchronized (Thread.currentThread()){
    //some code
}
Run Code Online (Sandbox Code Playgroud)

我不明白使用synchronized的原因currentThread.

两者之间有什么区别吗?

synchronized (Thread.currentThread()){
    //some code
}
Run Code Online (Sandbox Code Playgroud)

只是

//some code
Run Code Online (Sandbox Code Playgroud)

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

UPDATE

更详细的代码如下:

synchronized (Thread.currentThread()) {
       Thread.currentThread().wait(timeInterval);
}
Run Code Online (Sandbox Code Playgroud)

看起来就像Thread.sleep(timeInterval).这是真的吗?

Evg*_*eev 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);
    }
Run Code Online (Sandbox Code Playgroud)

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

  • 对于竞争条件来说,这似乎是一个很好的配方. (5认同)
  • 如果有人写这样的代码,我会请他解释他的行为.但是,是的,这是实际发生同步的一个例子. (5认同)

Mar*_*nik 9

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

synchronized (someSpecificThreadInstance) { ... }
Run Code Online (Sandbox Code Playgroud)

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


Iva*_*vov 6

基本上,synchronized块的存在与不存在之间没有区别.但是,我可以想到一种可能为这种用法带来其他意义的情况.

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

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

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

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

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

  • 依赖于硬性特定实现语义的未指定副作用肯定是不明智的.JMM仅指定当另一个线程获得*相同的锁*时会发生什么. (4认同)
  • 没有正确同步的内存屏障是没用的.在传递屏障时,任何其他线程可能在读取或写入共享变量之前或之后甚至之间没有任何控制,因此仍然产生不可预测的结果.这就是为什么允许JVM删除无效的`synchronized`块,从而也消除了内存障碍. (2认同)