Java 同步依赖于字符串初始化
我正在尝试使用字符串对象作为锁(是的,我知道不建议使用它-这只是为了理解锁) 在创建锁时初始化锁时,所有操作都正常: 代码: 日志: [线程555时间:2015/02/18 18:16:03]Java 同步依赖于字符串初始化,java,string,synchronization,Java,String,Synchronization,我正在尝试使用字符串对象作为锁(是的,我知道不建议使用它-这只是为了理解锁) 在创建锁时初始化锁时,所有操作都正常: 代码: 日志: [线程555时间:2015/02/18 18:16:03] [线程555睡得很好!时间:2015/02/18 18:17:43] [线程557时间:2015/02/18 18:17:43] [线程557睡得很好!时间:2015/02/18:19:23] [线程556时间:2015/02/18 18:19:23] [线程556睡得很好!时间:2015/02/18 1
[线程555睡得很好!时间:2015/02/18 18:17:43]
[线程557时间:2015/02/18 18:17:43]
[线程557睡得很好!时间:2015/02/18:19:23]
[线程556时间:2015/02/18 18:19:23]
[线程556睡得很好!时间:2015/02/18 18:21:03] 但是当我在方法中初始化实例成员锁时,就好像同步不起作用一样 代码: 日志: [线程555时间:2015/02/18 19:17:35]
[线程556时间:2015/02/18 19:17:40]
[线程556时间:2015/02/18 19:17:41]
[线程557时间:2015/02/18 19:17:44]
[线程560时间:2015/02/18 19:17:48]
[线程555睡得很好!时间:2015/02/18 19:19:15]
[线程556睡得很好!时间:2015/02/18 19:19:20]
[线程556睡得很好!时间:2015/02/18 19:19:21]
[线程557睡得很好!时间:2015/02/18 19:19:24]
[线程560睡得很好!时间:2015/02/18 19:19:28] 我不明白我启动目标的地方是什么地方?更多-它是一个字符串对象,所以无论如何它不应该是同一个引用?我错过了什么 编辑:是的,我不小心交换了代码。。当您多次收到“您的帖子似乎包含未正确格式化为代码的代码”,然后发现这是由于日志行引起的:|将修复交换 (您可能交换了代码片段) 我认为你错过了一个重要的方面。如果不使用字段
锁
作为锁,则将锁分配给实例。或如手册中所述:
与同步方法不同,同步语句必须指定提供内部锁的对象
因此,在您的第二个问题(问题代码片段中的第一个问题)中,您创建了一个新对象,即:
lock = new String("hello");
然后锁定该对象。这意味着每个线程都有一个它锁定的不同实例。所以根本就没有同步
示例:假设您有两个线程t1
和t2
。首先,我们运行线程1的一部分。线程一调用该方法并创建一个字符串实例,其值为“hello”
。所以内存看起来像:
+------+ +---------------+
|Action| <------------------+ thread memory |
+------+ +-------+ +---------------+
| lock------->|String |
+------+ +-------+
|"hello"|
+-------+
现在线程t2
进入。而t2
做的第一件事就是创建一个新对象:
+------+ +----------------+
|Action| <------------------+ thread2 memory | (old object)
+------+ +-------+ +----------------+ +-------+
| lock------->|String | |String | &
+------+ +-------+ +-------+
|"hello"| |"hello"|
+-------+ +-------+
+----++----------------+
|动作| |串| |串|&
+------+ +-------+ +-------+
|“你好”| |“你好”|
+-------+ +-------+
因此,新对象未锁定,线程t2
可以继续执行
然而,有可能您的代码确实会部分锁定。例如,线程t1
可能首先创建一个对象,然后设置Action
的字段,然后挂起。然后t2
变为活动状态,同时创建一个对象,设置操作的
字段,因此两个线程都以相同的对象结束,但这种可能性不大
最后要注意的是,使用“字符串”
作为锁“没有问题。首先,不要将字符串
用作锁,应该将其视为“锁定”字符串。因此,虚拟机只是将信息附加到它锁定的对象上。因此,没有好的或坏的对象可以被锁定。你所说的“当我初始化方法时”是什么意思。您可以发布为这两种情况创建和启动线程的代码吗?在第一个示例中,您的myAction
方法每次都创建单独的锁对象,因此我不会阻止其他线程使用它,因为使用此方法时,所有线程都将有新锁。在第二种情况下,只有一个线程能够进入synchronized块,因为在调用方法之前,将只存在一个创建的锁。换句话说,您可以从第一个代码示例中删除lock
和同步机制,您应该会得到相同的结果。@Moria:您是否意外地交换了这两个代码片段?您也可以执行Object lock=new Object()
。它避免了字符串的“不推荐”部分。@davidermann:老实说,我不明白为什么不建议将字符串作为锁
对象。既然字符串是不可变的,它确实不是很常见,但有理由不这样做……但我不明白的是:1)我从哪里开始有什么关系?成员不是静态的,在这两种情况下t1和t2都有各自“自己的”锁。2) 这怎么可能是每一个都有不同的字符串呢?如果它们使用相同的字符串值,那么应该引用内存中的相同对象,不是吗?谢谢关键是每次调用该方法时都要实例化一个新对象。因此,不是所有线程共享一个对象,而是每个线程生成自己的对象。
+------+ +---------------+
|Action| <------------------+ thread memory |
+------+ +-------+ +---------------+
| lock------->|String |
+------+ +-------+
|"hello"|
+-------+
+------+ +---------------+
|Action| <------------------+ thread memory |
+------+ +-------+ +---------------+
| lock------->|String | &
+------+ +-------+
|"hello"|
+-------+
+------+ +----------------+
|Action| <------------------+ thread2 memory | (old object)
+------+ +-------+ +----------------+ +-------+
| lock------->|String | |String | &
+------+ +-------+ +-------+
|"hello"| |"hello"|
+-------+ +-------+