简体   繁体   English

如何在Java中选择要锁定的对象?

[英]How to choose what object to lock on in Java?

I have read answers to similar questions about the correct ways to use synchronized. 我已经阅读了有关使用synchronized的正确方法的类似问题的答案。 However, they dont seem to explain why this issue occurred. 但是,他们似乎没有解释为什么会出现这个问题。 Even though I added synchronized to my getValue and setValue method, i still get outputs like the following. 即使我将synchronized添加到我的getValue和setValue方法,我仍然得到如下输出。 Why does this happen? 为什么会这样?

output: 输出:

making set 制作一套

Doing get 做得到

making set 制作一套

Doing making set 做套装

get 得到

Doing get 做得到

making set 制作一套

making Doing get 做得到

set

Code: 码:

package src;

public class StackNode {
private Object value;
private StackNode next;

private final Object lock = new Object();

public StackNode() {
    setValue(null);
    setNext(null);
}

public StackNode(Object o) {
    value = 0;
    next = null;
}

public StackNode(StackNode node) {
    value = node.getValue();
    next = node.getNext();
}

public synchronized Object getValue() {
        System.out.print(" Doing ");
        System.out.println(" get ");
        System.out.flush();
        return value;

}

public  synchronized void setValue(Object value) {
        System.out.print(" making ");
        System.out.println(" set ");
        System.out.flush();
        this.value = value;
}

public synchronized StackNode getNext() {
    return next;
}

public synchronized void setNext(StackNode next) {
    this.next = next;
}
}

Test: 测试:

public class TestStackNode {
private final static StackNode node = new StackNode();

    @Test
public void getSetValueTest() throws InterruptedException{
    node.setValue("bad");
    Runnable setValue = new Runnable(){
        @Override
        public void run() {
            node.setNext(new StackNode());
            node.setValue("new");
        }
    };

    Runnable getValue = new Runnable(){
        @Override
        public void run() {
            Assert.assertEquals("new", node.getValue());
        }
    };
    List<Thread> set = new ArrayList<Thread> ();
    List<Thread> get = new ArrayList<Thread> ();
    for (int i = 0; i < 30000; i++){
        set.add( new Thread(setValue));
        get.add(new Thread(getValue));
    }

    for (int i = 0; i < 30000; i++){
        set.get(i).start();
        get.get(i).start();
    }

    for (int i = 0; i < 30000; i++){
        set.get(i).join();
        get.get(i).join();
    }
}

This should fix the issue. 这应该解决问题。

public Object getValue() {
  synchronized(System.out){
    System.out.print(" Doing ");
    System.out.println(" get ");
    System.out.flush();
    return value;
  }

}

The problem is that your no-arg constructor calls setValue(...) on the newly-created instance: 问题是你的no-arg构造函数在新创建的实例上调用setValue(...)

public StackNode() {
    setValue(null);
    setNext(null);
}

and your Runnable setValue constructs a new instance of StackNode , to pass to node.setNext(...) : 并且您的Runnable setValue构造一个新的StackNode实例,以传递给node.setNext(...)

            node.setNext(new StackNode());

(even though your test never actually uses node.next , so this is essentially a no-op aside from the output it produces). (即使你的测试从未真正使用过node.next ,所以除了它产生的输出之外,这实际上是一个无操作)。 Since your synchronized methods are instance methods (not static methods), they have separate locks, which means that the call to setValue(...) in the new instances' constructors is not synchronized with respect to the calls you make on node . 由于您的synchronized方法是实例方法(不是static方法),因此它们具有单独的锁,这意味着在新实例的构造函数中对setValue(...)的调用与您在node上进行的调用不同步。

Note that, although your specific problem is rather unusual (you have a getter and setter that are manipulating shared external state, namely System.out , but do not have any corresponding shared locks to prevent interference), it is actually always a bad idea to call a method from a constructor, unless the method is private or final or static or the class is final , because a superclass constructor is called before a subclass instance is fully created, so if the constructor calls a method that's overridden in a subclass, the subclass method will receive an incomplete this object and might misbehave terribly. 请注意,虽然您的特定问题相当罕见(您有一个getter和setter正在操作共享外部状态,即System.out ,但没有任何相应的共享锁来防止干扰),实际上总是一个坏主意从构造函数调用方法,除非该方法是privatefinalstatic或者类是final ,因为在完全创建子类实例之前调用超类构造函数,因此如果构造函数调用在子类中重写的方法,子类方法将收到一个不完整的this对象,并可能行为异常严重。 You're better off changing your constructor to this: 你最好将构造函数更改为:

public StackNode() {
    value = null;
    next = null;
}

(or just remove the assignment statements altogether, since fields of reference type are automatically initialized to null anyway). (或者只是完全删除赋值语句,因为引用类型的字段无论如何都会自动初始化为null )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM