【问题标题】:Java Thread Shared Object Synchronization IssueJava 线程共享对象同步问题
【发布时间】:2009-07-16 13:10:26
【问题描述】:

我遇到了 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();

    }
}

偶尔我会得到“在利比亚线程刚果很酷” 这不应该发生。我只期望: “在利比亚线程利比亚真棒” “在刚果线程刚果酷”

我不希望它们混在一起。

【问题讨论】:

标签: java multithreading volatile synchronized


【解决方案1】:

为什么不混合?尽管每个单独的调用都是同步的,但没有什么可以阻止一个线程调用 v.setValue,然后是 other 线程调用 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。

诚然,这不是一个理想的设计 - 但我猜这个演示比其他任何东西都更能理解同步 :)

【讨论】:

  • 锁定“真实”对象通常是不好的做法。使用一个单独的对象来锁定,这个对象应该只用于锁定一个特定的功能。
  • 是的,我同意...不过我希望这只是一个教学示例。
【解决方案2】:

接下来会发生什么:

  1. 线程 1 设置值
  2. 线程 2 设置值
  3. 线程 1 读取线程 2 设置的值。

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

每个线程的实现都会像下面这样,注意它们需要有完全相同的对象!

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() );
            }
        }
    }
}

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 2016-05-03
      • 2023-03-14
      • 2012-06-28
      • 2014-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-13
      相关资源
      最近更新 更多