Java线程共享对象同步问题

Java线程共享对象同步问题,java,multithreading,volatile,synchronized,Java,Multithreading,Volatile,Synchronized,我发现Synchronized的行为与我预期的不一致,我尝试使用volatile关键字: 共享对象: public class ThreadValue { private String caller; private String value; public ThreadValue( String caller, String value ) { this.value = value; this.caller = caller; } public synchronized S

我发现Synchronized的行为与我预期的不一致,我尝试使用volatile关键字:

共享对象:


public class ThreadValue {
private String caller;
private String value;
public ThreadValue( String caller, String value ) {
    this.value = value;
    this.caller = caller;
}

public synchronized String getValue() {
    return this.caller + "     "  + this.value;
}
public synchronized void setValue( String caller, String value ) {
    this.caller = caller;
    this.value = value;
}
}
线程1:


class CongoThread implements Runnable {
    private ThreadValue v;
    public CongoThread(ThreadValue v) {
    this.v = v;

    }
    public void run() {
    for (int i = 0; i  10; i++) {
    v.setValue( "congo", "cool" );
    v.getValue();
    }
    }
}
线程2:


class LibyaThread implements Runnable {
    private ThreadValue v;
    public LibyaThread(ThreadValue v) {
    this.v = v;

    }
    public void run() {
    for (int i = 0; i  10; i++) {
       v.setValue( "libya", "awesome" );
       System.out.println("In Libya Thread " + v.getValue() );

    }
    }
}

主叫班级:


class TwoThreadsTest {
    public static void main (String args[]) {

    ThreadValue v = new ThreadValue("", "");
        Thread congo = new Thread( new CongoThread( v ) );
        Thread libya = new Thread( new LibyaThread( v ) );

    libya.start();
        congo.start();

    }
}
偶尔我会说“在利比亚很酷” 这是不应该发生的。我只希望: “在利比亚,这太棒了” “在刚果,刚果很酷”


我不希望它们是混合的。

问题是,利比亚线程的v.getValue()可能在刚果线程调用v.setValue()之后立即被调用,从而导致您的混淆


解决方案是让一个线程同时阻塞get和set值,否则您将继续遇到这个问题。您必须在setter内部调用getter,或者使用wait/notify让另一个线程等待,直到一个线程同时设置并获取了值。

为什么它们不能混合使用?尽管每个单独的调用都是同步的,但是没有什么可以阻止一个线程调用v.setValue,然后另一个线程调用setValue,然后第一个线程调用getValue()。我相信这就是正在发生的事情。您可以使用以下方法避免此问题:

public void run() {
    for (int i = 0; i  10; i++) {
       synchronized (v) {
           v.setValue( "libya", "awesome" );
           System.out.println("In Libya Thread " + v.getValue() );
       }
    }
}
这样,在每次迭代中,它确保调用
setValue
getValue
,而同时没有另一个线程调用
setValue


诚然,这不是一个理想的设计——但我猜这个演示更能理解同步:)

发生了以下情况:

  • 线程1设置该值
  • 线程2设置该值
  • 线程1读取线程2设置的值
  • 为了解决这个问题,需要有一个lock对象来保护两个线程的get/set函数调用。最好的方法是创建一个额外的同步方法,该方法同时执行set和get。然而,有时这是不可取的。在这种情况下,给两个线程一个lock对象。这只是一个普通的物体。然后在同步块中使用

    每个线程的实现如下所示,注意它们需要具有完全相同的对象

    Object lockObject = new Object();
    Thread t1 = new CongroThread(v, lockObject);
    Thread t2 = new LibyaThread(v, lockObject);
    
    ...
    
    class CongoThread implements Runnable {
        private ThreadValue v;
        private Object lockObject;
    
        public CongoThread(ThreadValue v, Object lockObject) {
        this.v = v;
        this.lockObject = lockObject,
        }
        public void run() {
            for (int i = 0; i < 10; i++) {
                synchronized(lockObject)
                {
                    v.setValue( "libya", "awesome" );
                    System.out.println("In Libya Thread " + v.getValue() );
                }
            }
        }
    }
    
    对象锁定对象=新对象();
    线程t1=新的Congreothread(v,lockObject);
    线程t2=新的LibyaThread(v,lockObject);
    ...
    类CongoThread实现可运行{
    私有价值v;
    私有对象;
    public CongoThread(线程值v,对象锁定对象){
    这个,v=v;
    this.lockObject=lockObject,
    }
    公开募捐{
    对于(int i=0;i<10;i++){
    已同步(锁定对象)
    {
    v、 setValue(“利比亚”,“很棒”);
    System.out.println(“In-Thread”+v.getValue());
    }
    }
    }
    }
    
    锁定“真实”对象通常是不好的做法。使用一个单独的对象来锁定,这个对象应该只用于锁定一个特定的功能。是的,我同意。。。我希望这只是一个教学例子。和的重复已经被回答了多次。@Bombe第二个“重复”就是这个问题