Java 同步(对象锁定)未按预期工作
Java synchronization(object locking) not working as expected
请考虑以下代码。
import static java.lang.System.out;
public class Task
{
public Integer k = new Integer(10) ;
public Task()
{
out.println(k + " constructor of Task : " + Thread.currentThread().getName());
}
}
import static java.lang.System.out;
public class Executor2 implements Runnable
{
private Task task;
public Executor2(Task t)
{
out.println("constructor of Executor2 : " + Thread.currentThread().getName());
task = t;
}
@Override
public void run()
{
synchronized(task.k)
{
task.k = 88;
out.println("changed value of task.k to : " + task.k + " " + Thread.currentThread().getName());
try
{
out.println("sleeping");
Thread.sleep(5000);
}
catch (InterruptedException ex)
{
ex.printStackTrace(out);
}
out.println("done");
}
}
}
import static java.lang.System.out;
public class Executor3 implements Runnable
{
private Task task;
public Executor3(Task t)
{
out.println("constructor of Executor3 : " + Thread.currentThread().getName());
task = t;
}
@Override
public void run()
{
synchronized(task.k)
{
task.k = 888;
out.println("changed value of task.k to : " + task.k + " " + Thread.currentThread().getName());
}
}
}
------------------------------------------------------------
public class Main
{
public static void main(String[] args)
{
Task task = new Task();
Executor2 executor2 = new Executor2(task);
Thread thread2 = new Thread(executor2);
thread2.start();
Executor3 executor3 = new Executor3(task);
Thread thread3 = new Thread(executor3);
thread3.start();
}
}
下面是程序的输出。
10 Task 的构造函数:main
Executor2 的构造函数:main
Executor3 的构造函数:main
将 task.k 的值更改为:88 Thread-0
睡觉
将 task.k 的值更改为:888 Thread-1
完成
这里令人惊讶的是输出行:将 task.k 的值更改为:888 Thread-1 预计不会在输出行之前打印:完成。
为什么 Integer 对象上的锁在睡眠持续时间结束之前就被释放了?
谢谢。
synchronized(task.k)
{
task.k = 888;
更改同步对象会破坏同步点。然后,您尝试在旧对象上保持同步的同时打印新对象。不要替换线程正在同步的对象!
扩展 David Schwartz 所说的:在几乎所有情况下,如果您要写这个:
synchronized (foo) {
...
}
那么引用锁对象的变量应该是一个final
字段:
class MyClass {
final Object foo = new Object();
...
void someMethod(...) {
synchronized (foo) {
...
}
}
...
}
这将防止您以后犯同样的错误。
同意楼上的说法。此外,在使用私有锁时,一个陷阱是不锁定字符串文字——字符串文字是共享资源。
private final String lock = “xx”;
private final String lock = new String(“xxx”);
第二次加锁就可以了
请考虑以下代码。
import static java.lang.System.out;
public class Task
{
public Integer k = new Integer(10) ;
public Task()
{
out.println(k + " constructor of Task : " + Thread.currentThread().getName());
}
}
import static java.lang.System.out;
public class Executor2 implements Runnable
{
private Task task;
public Executor2(Task t)
{
out.println("constructor of Executor2 : " + Thread.currentThread().getName());
task = t;
}
@Override
public void run()
{
synchronized(task.k)
{
task.k = 88;
out.println("changed value of task.k to : " + task.k + " " + Thread.currentThread().getName());
try
{
out.println("sleeping");
Thread.sleep(5000);
}
catch (InterruptedException ex)
{
ex.printStackTrace(out);
}
out.println("done");
}
}
}
import static java.lang.System.out;
public class Executor3 implements Runnable
{
private Task task;
public Executor3(Task t)
{
out.println("constructor of Executor3 : " + Thread.currentThread().getName());
task = t;
}
@Override
public void run()
{
synchronized(task.k)
{
task.k = 888;
out.println("changed value of task.k to : " + task.k + " " + Thread.currentThread().getName());
}
}
}
------------------------------------------------------------
public class Main
{
public static void main(String[] args)
{
Task task = new Task();
Executor2 executor2 = new Executor2(task);
Thread thread2 = new Thread(executor2);
thread2.start();
Executor3 executor3 = new Executor3(task);
Thread thread3 = new Thread(executor3);
thread3.start();
}
}
下面是程序的输出。
10 Task 的构造函数:main
Executor2 的构造函数:main
Executor3 的构造函数:main
将 task.k 的值更改为:88 Thread-0
睡觉
将 task.k 的值更改为:888 Thread-1
完成
这里令人惊讶的是输出行:将 task.k 的值更改为:888 Thread-1 预计不会在输出行之前打印:完成。 为什么 Integer 对象上的锁在睡眠持续时间结束之前就被释放了?
谢谢。
synchronized(task.k)
{
task.k = 888;
更改同步对象会破坏同步点。然后,您尝试在旧对象上保持同步的同时打印新对象。不要替换线程正在同步的对象!
扩展 David Schwartz 所说的:在几乎所有情况下,如果您要写这个:
synchronized (foo) {
...
}
那么引用锁对象的变量应该是一个final
字段:
class MyClass {
final Object foo = new Object();
...
void someMethod(...) {
synchronized (foo) {
...
}
}
...
}
这将防止您以后犯同样的错误。
同意楼上的说法。此外,在使用私有锁时,一个陷阱是不锁定字符串文字——字符串文字是共享资源。
private final String lock = “xx”;
private final String lock = new String(“xxx”);
第二次加锁就可以了