Java 同步块在构造函数中的用途是什么?
我们不能使构造函数Java 同步块在构造函数中的用途是什么?,java,synchronization,Java,Synchronization,我们不能使构造函数同步,但可以在构造函数内部编写同步。在什么情况下会有这样的要求? 我觉得好笑 package com.simple; public class Test { public Test() { synchronized (this) { System.out.println("I am called ..."); } } public static void main(String[] args) {
同步
,但可以在构造函数内部编写同步
。在什么情况下会有这样的要求?
我觉得好笑
package com.simple;
public class Test {
public Test() {
synchronized (this) {
System.out.println("I am called ...");
}
}
public static void main(String[] args) {
Test test=new Test();
System.out.println(""+test);
}
@Override
public String toString() {
return "Test []";
}
}
那么,您可以在构造函数中启动一个新线程。这将是非常不寻常的-当然在您提供的代码中,这将是毫无意义的-但它可能会发生
语言通常不会试图找到你能做的每一件毫无意义的事情——这会导致一个非常复杂的语言规范。语言用户也必须有一定程度的思考…在
上同步此将是一种不良做法的迹象,因为这将意味着您正在从构造函数中泄漏此
:这是在同一对象上同步其他代码的唯一方法
然而,在其他一些公共锁上进行同步可能是合法的:构造函数my确实涉及调用一些需要这种同步的代码。可能是您正在更改构造函数中由多个线程访问的一些公共数据。尽管在正常情况下,最好采用更好、更简单的方法。但您没有理由这样做
但是,如果让this
引用“转义”构造函数(当然,这是一种糟糕的做法),那么您可能希望强制客户机代码在调用其他操作之前等待同步块完成
例如:
class C {
public C() {
// ....
synchronized(this) {
someService.doSomethingWith(this);
// some other critical stuff...
}
}
public synchronized void criticalSection() {
// ...
}
}
在本例中,如果在someService
中调用criticalSection()
,将强制您等待,直到构造函数中的同步块完成
但同样,这是不推荐的,您不应该允许这个逃逸构造函数。据我所知,如果您处理构造函数中的静态字段,这可能是至关重要的。当对象正在构造时,只有正在创建它的线程才有权访问该对象,但如果构造函数中的静态字段值发生更改,则可能会出现问题,因为两个不同的线程可以同时创建同一类的对象,从而导致与静态字段相关的冲突。但不确定使用它来锁定是否是个好主意,它可以用来确保构造函数中非最终字段的安全发布
public class Test {
int a;
public Test() {
synchronized (this) {
a = 5;
}
}
如果另一个线程接收到一个类型为Test的对象,并且它也在Test
的实例上同步,那么这将在构造函数中的synchronized
块的结尾与另一个线程中的synchronized
块的开头之间创建一个before关系:
public class InOneThread {
public void run() {
InAnotherThread.test = new Test();
}
}
public class InAnotherThread {
public static Test test;
public void run() {
if (test == null) {
// Because assignment to `test` wasn't safely published, the
// field could be null even if it was assigned "earlier" in real
// time.
return;
}
synchronized(test) {
System.out.println(test.a);
// If no more modifications were made to 'a', this prints 5
// (guaranteed). Without the synchronized blocks, this could
// print 0 (zero) or 5.
}
}
}
然而,在实践中,这几乎从来没有用过,因为您需要使用同步机制来安全地将测试实例从一个线程传递到另一个线程,而且同步机制本身几乎肯定已经在线程之间引入了一种先发生后发生的关系
尽管如此,它仍然可以在某些高度特定的并发组件中使用。我认为Java灵活性允许这样做,因为在构造函数中,您可以像普通方法一样做几乎所有事情(),但是,如果我们这样做,这是一种不好的做法。
这是一个很好的实践,我们不应该在构造函数中使用同步块,在构造函数中使用同步块是最好的做法。让我问你一个问题。例如,我们需要从构造函数安全地发布线程安全的对象,比如说,在中。这样做安全吗?我的意思是,在构造函数中使用synchronized(this)
block是否允许this
对象转义或什么…?@St.Antario:我看不出这是如何在构造函数中发布对象的。它正在创建它,但在构造函数完成之前,其他任何东西都无法看到该对象。听起来你可能想问一个新问题。很有趣的一点。你能解释一下泄漏的原因吗?我看不出构造函数中的同步(this)
如何允许this
转义。示例:。这里的逃生之路在哪里?@St.Antario他说,synchornized(这个)
的唯一原因是泄漏。但我认为这并不总是正确的,因为synchronized
还可以用于创建与构造函数完成后在另一个线程中发生的事情的发生之前关系。请看我对这个问题的回答。即使在数据竞争中,它所赢得的是安全的发布。但由于它依赖于对象的接收者显式地获取锁,这破坏了上面的基本假设:您希望对您无法控制的代码进行万无一失的保护。我不知道如何进行测试。
可以在没有同步块的情况下打印0<在构造函数完成执行之前,不会分配代码>测试
,因此测试
将为null
或test。a
将为5。。。“我错了吗?”MarkoTopolnik这个问题也许有一个更好的例子;您可以将构造函数和访问变量的代码封装在同一个容器中class@Codebender如果没有同步,这是一个错误。默认变量的写入保证对所有线程都是可见的,但随后的5赋值不是没有同步操作的。因此,JVM和编译器不需要在任何其他线程中同时显示5的赋值,直到在编写5的线程和读取test的线程之间建立关系之前发生。a
如果这样做,那么线程之间的互斥就没有什么好处了。简单地说,除非所有字段都是final
或volatile
,否则您无法在数据竞争中提供安全发布的良好机制。就我所关注的关于计划中的新Java内存模型的讨论而言,它将删除final
字段的特殊状态,并提供
public class Test {
int a;
public Test() {
synchronized (this) {
a = 5;
}
}