我有以下课程:

public class BoolFlag
{
boolean flag;

public BoolFlag()
{
    flag=false;
}

public synchronized void setFlag(boolean flag)
{
    this.flag=flag;
}

public synchronized boolean getFlag()
{
    return flag;
}
}


如果我没有将getter和setter设置为同步,则会导致如下问题:

当我在一个按钮中设置一个名为imageready的BoolFlag对象时,单击listiner:

 if (status == JFileChooser.APPROVE_OPTION)
           {
               try
               {
                    System.out.println("File chosen");
                    imgFile=chooser.getSelectedFile();
                    image.setImg( ImageIO.read(imgFile) );
                    imageready.setFlag(true);

               }
               catch(Exception e)
               {
                   System.out.println("file not opened");
                }
            }


然后在另一个线程中获取它:

public void run()
{
    boolean running=true;
    while(running)
    {
        // System.out.println("Img sender running");
        if(imageready.getFlag())
        { System.out.println("trying to send img");
            try
            {
                  OutputStream outputStream = img_s.getOutputStream();
                  ImageIO.write(image.getImg(), "jpg", outputStream);

                  System.out.println("Send image"+System.currentTimeMillis());
                  outputStream.flush();
                  outputStream.close();
                  imageready.setFlag(false);

            }
            catch(Exception e)
            {
                System.out.println("image client send failed");
                imageready.setFlag(false);
            }
        }
     }
 }


我期望的是在选择文件之后,应将imageready设置为true,然后获得标志的线程将在块内执行语句:

if(imageready.getFlag()){...send image...}


但是,即使imageready设置为true,它也不会进入程序段。

您可能会注意到,在if块之前有一条语句被注释掉了:

// System.out.println("Img sender running");


如果我不对其进行注释,则将执行if块中的语句。放置其他不相关的语句(如sleep(100))可能会产生相同的效果。如果我在if(imageready.getflag())处放置一个断点,然后逐步执行它,它也会进入该块中。

如果将getter和setter设置为同步,则不会发生这些问题。
似乎这与Tread-safe类有关,但是即使我只使用getter和setter也无法弄清楚为什么这很重要。

最佳答案

您的get访问器很可能是内联的,因此实际上只是一个字段读取,而运行时可能会优化掉循环中出现的简单字段读取,从而使该值实际上只能读取一次。

您可以通过读取volatile值来避免这种情况,或者如您所注意到的,通过在写入和读取标志时使用synchronized来强制执行内存屏障。

在您的情况下,我只用AtomicBoolean代替您的BoolFlag类。

附带一提,我会避免在第二个线程上忙于“图像就绪”标志。考虑使用等待通知机制,例如计数为CountDownLatch1

09-26 15:14