我有以下课程:
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
类。
附带一提,我会避免在第二个线程上忙于“图像就绪”标志。考虑使用等待通知机制,例如计数为CountDownLatch
的1
。