【问题标题】:Why is synchronized not working?为什么同步不起作用?
【发布时间】:2016-03-23 19:23:20
【问题描述】:

我正在尝试编写一种方法,该方法向设备询问输入然后接受响应,所有这些都是原子操作。

这是我的代码(query 方法确实应该重点关注):

public class DeviceConnection implements Runnable{
    //For query
    static int test = 0;

    //For writeline
    static PrintWriter out = null; //(initialized in constructor)

    //Only important for readline
    static String[] systemMessage=new String[10];
    static int messageIn=0;
    static int messageOut=0;
    static boolean connected = false;
    static boolean endConnect = true;

    static PrintStream logWriter; //(initialized in constructor)
    static String serverName="";//(initialized in constructor)
    static int socketNum;//(initialized in constructor)

    /** REALLY ONLY NEED TO LOOK AT THIS METHOD
         * Atomic operation to ask for data from device
         * and get a response.
         * @param line -    query to be passed to device
         * @return response from device
         */
    public synchronized String query(String line){
        int temp = test;
        System.err.print("foo" + test);
        System.err.print(this);
        String response;
        writeLine(line);
        response = readLine();
        System.err.println("bar" + test + " should be " + temp);
        test = temp+1;
        return response;
    }


/**
     * Writes a query to the device. 
     *<p>
     * Does <b>not</b> get or handle any response
     * @param line -    query to be passed to device
     */
    public synchronized void writeLine(String line) {
        out.println(line + "\n");
    }

/**
     * Reads a response from device.
     *<p>
     * Should never be used outside of <code>query</code>
     * as this could create a race condition if another
     * thread is writing to the device at the same time.
     * @return
     */
    private synchronized String readLine() {

        String response;
        long start, stop;

        if (messageIn != messageOut) { // new message exists
            response = systemMessage[messageOut];
            messageOut = (messageOut + 1) % 10;
        } else {
            start = System.currentTimeMillis();
            try {
                if (connected) { // if connected wait for heatbeats
                    //wait(15000);
                    wait();
                    start = System.currentTimeMillis();
                } else { // if not connected ignore heartbeats
                    wait();
                    start = System.currentTimeMillis();
                }
            } catch (InterruptedException e) { return "Interrupted"; }
            stop = System.currentTimeMillis();

            if (stop - start < 12000) { // heart beats running at 5 s
                if (messageIn != messageOut) { // new message exists
                    response = systemMessage[messageOut];
                    messageOut = (messageOut + 1) % 10;
                } else {
                    return null;
                }
            } else { // heart beats lost
                response = "Heart beats lost";
                logWriter.println(response);
                if (connected) { // connection lost on client side
                    logWriter.println("Connection to " + serverName + 
                                      " lost on client side");
                    sleep(60000);
                    connect(serverName,socketNum);
                }
            }
        }
        return response;
    }
}

通常query 方法运行良好,但有时我会得到如下输出:

foo59lib.DeviceConnection@7bd0bf6d(other System.out stuff printed here)
foo59lib.DeviceConnection@7bd0bf6dbar59 should be 59
bar60 should be 59

这怎么可能?方法不是在对象上锁定/同步吗?该对象显然与打印显示的相同,但不知何故同时执行了两个 query 方法。

【问题讨论】:

  • 你有多少个 DeviceConnection 类的实例?
  • 其实是一个单例类,所以只有一个。然而,这甚至不重要,因为输出清楚地表明这两种方法都是从同一个对象调用的(使用 id:DeviceConnection@7bd0bf6d

标签: java multithreading thread-safety synchronized


【解决方案1】:

您的readLine 方法(从query 调用)调用wait,该方法释放锁,这将使另一个线程能够同时调用query

您应该始终使用条件变量在循环内调用wait,您使用 if 来决定是否等待的模式是有缺陷的。一旦你的线程重新获得锁,它就需要检查当前状态是什么。

wait 释放锁的说明在Object#wait 的文档中进行了说明:

当前线程必须拥有这个对象的监视器。线程 释放此监视器的所有权并等待另一个线程 通知在此对象的监视器上等待的线程唤醒 通过调用 notify 方法或 notifyAll 方法。这 然后线程等待,直到它可以重新获得监视器的所有权,然后 恢复执行。

与单参数版本一样,中断和虚假唤醒是 有可能,而且这个方法应该一直循环使用:

 synchronized (obj) {
     while (<condition does not hold>)
         obj.wait();
     ... // Perform action appropriate to condition
 }

【讨论】:

  • 认为我现在可以正常工作了。结果我想同步/等待已经 synchronized 方法内的另一个对象,以便我的线程可以 wait 处理一个条件,而不允许其他线程开始共同执行 query 方法。
猜你喜欢
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
  • 2019-12-13
  • 2011-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-17
相关资源
最近更新 更多