Java 为什么可以';当只使用getter和setter时,线程不是不安全的类工作吗?
我的课程如下:Java 为什么可以';当只使用getter和setter时,线程不是不安全的类工作吗?,java,multithreading,thread-safety,synchronized,Java,Multithreading,Thread Safety,Synchronized,我的课程如下: 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设置为同步,则会导致以下问
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设置为同步,则会导致以下问题:
public class BoolFlag
{
boolean flag;
public BoolFlag()
{
flag=false;
}
public synchronized void setFlag(boolean flag)
{
this.flag=flag;
}
public synchronized boolean getFlag()
{
return flag;
}
}
当我在一个按钮中设置名为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安全类的,但我不明白为什么它很重要,即使我只是使用getter和setter。您的get访问器很可能是内联的,因此它实际上只是一个字段读取,并且运行时可以优化掉出现在循环中的简单字段读取,从而使值有效地只读取一次 您可以通过读取
volatile
值来避免这种情况,或者如您所注意到的,在写入和读取标志时,使用synchronized
强制设置内存障碍
在您的情况下,我只需要使用AtomicBoolean
来代替BoolFlag
类
另一方面,我会避免在第二个线程上忙着旋转“image ready”标志。考虑使用一个等待通知机制,例如,<代码> CurtDutLabCH < /C> >计数>>1 /代码> .< /P> < P>是的,这是由于线程通信,而您所要实现的是: 您的代码有几个问题: 1) 写入映像文件的线程类在
getFlag()
方法中没有被阻止,因此while循环可能会在保持线程活动的情况下大量运行。相反,在getFlag()
中阻塞线程,并使用wait()
和notifyAll()
方法进行有效的线程通信
。在您的情况下,您需要做类似的事情来管理线程。同步没有提供编写线程安全类的便利。在多线程环境中,如果一个对象在多个线程之间共享,则不存在任何可能影响程序正确性的问题,如数据争用、脏读、过时值 为了优化代码,java将变量数据保存在内存缓冲区中,以便更快地访问。由于其他线程对该变量所做的更改可能不会立即反映出来,这就是为什么其他线程读取该变量以前的值,因此无法在不久的将来读取更新后的值
您可以将变量定义为volatile。Volatile请确保线程将获得Java内存模型提交的变量最近更新的值。但是if(imageready.getFlag())将保持运行,并保持检查imageready是否更改为true,因为它在线程的while(true)循环中。那么为什么它不能得到imageready的更新值呢?谢谢。正如我所说,每个线程都有自己的堆栈区域和存储在寄存器中的值,所以一旦捕获到它,它可能无法获得更新的值……在生产者-消费者问题中,生产者和消费者会修改同一个对象。但我的情况有点不同:一个线程正在修改imageready的值,而另一个线程只是在不更改其值的情况下获取它。