Java 线程多久执行一次?我的观察者模式出错了?

Java 线程多久执行一次?我的观察者模式出错了?,java,multithreading,Java,Multithreading,下面是我当前代码的简化版本。我很确定我在语法方面没有做错任何事情,我也找不到我在概念上的错误 这是我试图实现的一种观察者模式。我负担不起从Java.utils.observable继承,因为我的类已经很复杂,并且是从另一个类继承的。 这里有两个部分: 有一个通知程序类实现Runnable: public class Notifier implements Runnable{ public void run() { while(true) {

下面是我当前代码的简化版本。我很确定我在语法方面没有做错任何事情,我也找不到我在概念上的错误

这是我试图实现的一种观察者模式。我负担不起从Java.utils.observable继承,因为我的类已经很复杂,并且是从另一个类继承的。 这里有两个部分:

有一个通知程序类实现Runnable:

public class Notifier implements Runnable{

    public void run()
    {
        while(true)
        {
            MyDataType data = getData();
            if(data.isChanged()==true)
            {
                refresh();
            }
        }
    }
}
然后是我的主类,它需要响应对MyDataType数据的更改

public class abc {

    private MyDataType data;

    public void abc(){
               Notifier notifier = new Notifier();
               Thread thread = new Thread(notifier);
               thread.start();
          }     


    public MyDataType getData(){
              return this.data;
    }


    public void refresh(){
         MyDatatype data = getData();
     //Do something with data
    }
}
问题:发生的情况是,当“数据”更改时,通知程序正在调用refresh()。然而,在refresh()中,当我执行getData()时,我得到的是“data”的旧版本! 我应该提到,代码的其他部分也在调用refresh()函数

  • 我忽略了什么
  • 这个问题还有其他更好的解决方案吗
  • 如果我不能应用现成的默认Java实现,我应该如何设计主题观察者系统
当我执行getData()时,我得到的是“data”的旧版本

您的
数据
字段在多个线程之间共享,因此必须使用
volatile
关键字对其进行标记

private volatile MyDataType data;
这会在读写操作周围形成“内存屏障”,使所有线程都能看到该值。即使通知程序线程正在调用
getData()
,如果
data
的内存缓存不可用,也会检索出它的值。如果没有内存屏障,
数据
值将随机更新或从不更新

正如@JB在评论中提到的,
volatile
保护您不被重新分配
数据
字段。如果更新当前
数据
值中的一个字段,则不会跨越内存屏障,通知程序的内存将不会更新

回顾您的代码,看起来是这样的:

if(data.isChanged()==true)
{
    refresh();
}
如果未将
数据
分配给新对象,则将
数据
设置为
易失性
对您没有帮助。你必须:

  • 设置某种
    易失性布尔脏数据
    更新时,代码>字段
  • 每次在
    同步
    块中更新或读取
    数据
当我执行getData()时,我得到的是“data”的旧版本

您的
数据
字段在多个线程之间共享,因此必须使用
volatile
关键字对其进行标记

private volatile MyDataType data;
这会在读写操作周围形成“内存屏障”,使所有线程都能看到该值。即使通知程序线程正在调用
getData()
,如果
data
的内存缓存不可用,也会检索出它的值。如果没有内存屏障,
数据
值将随机更新或从不更新

正如@JB在评论中提到的,
volatile
保护您不被重新分配
数据
字段。如果更新当前
数据
值中的一个字段,则不会跨越内存屏障,通知程序的内存将不会更新

回顾您的代码,看起来是这样的:

if(data.isChanged()==true)
{
    refresh();
}
如果未将
数据
分配给新对象,则将
数据
设置为
易失性
对您没有帮助。你必须:

  • 设置某种
    易失性布尔脏数据
    更新时,代码>字段
  • 每次在
    同步
    块中更新或读取
    数据

首先,您的
数据
变量可能会被缓存,因此您始终需要通过将其设置为
易失性
来获取最新值

第二,你在这里做的是一个模式。此模式通常最好通过消息实现。当您收到新数据时,您可以创建一个不可变的对象并将其发布到使用者线程(通过一个线程安全队列,如a)而不是共享变量

大致如下:

public class Notifier extends Thread{
   private BlockingQueue<E> consumerQueue = null;
   public setConsumerQueue(BlockingQueue<E> val){
      consumerQueue = val;
   }
   // main method where data is received from socket...
   public void run(){
      while(!interrupted()){
           data = ... // got new data here
           if(!data.isChanged()) continue;
           // Post new data only when it has changed
           if(consumerQueue!=null) consumerQueue.offer(data);
      }
   }
}

public class Consumer extends Thread{
   private BlockingQueue<E> consumerQueue = new BlockingQueue<E>();
   public Consumer (Producer val){
      val.setConsumerQueue(consumerQueue);
   }
   public void run(){
      while(!interrupted()){
           data = consumerQueue.take();// block until there is data from producer
           if(data !=null) processData(data);
      }
   }
}
公共类通知程序扩展线程{
private BlockingQueue consumerQueue=null;
public setConsumerQueue(阻塞队列val){
consumerQueue=val;
}
//从套接字接收数据的主要方法。。。
公开募捐{
而(!interrupted()){
data=…//这里有新数据
如果(!data.isChanged())继续;
//仅在新数据发生更改时发布新数据
if(consumerQueue!=null)consumerQueue.offer(数据);
}
}
}
公共类使用者扩展线程{
private BlockingQueue consumerQueue=新建BlockingQueue();
公共消费者(生产者val){
val.setConsumerQueue(consumerQueue);
}
公开募捐{
而(!interrupted()){
data=consumerQueue.take();//阻塞,直到有来自生产者的数据
if(data!=null)processData(data);
}
}
}

首先,您的
数据
变量可能会被缓存,因此您始终需要通过将其设置为
易失性
来获取最新值

第二,你在这里做的是一个模式。此模式通常最好通过消息实现。当您收到新数据时,您可以创建一个不可变的对象并将其发布到使用者线程(通过一个线程安全队列,如a)而不是共享变量

大致如下:

public class Notifier extends Thread{
   private BlockingQueue<E> consumerQueue = null;
   public setConsumerQueue(BlockingQueue<E> val){
      consumerQueue = val;
   }
   // main method where data is received from socket...
   public void run(){
      while(!interrupted()){
           data = ... // got new data here
           if(!data.isChanged()) continue;
           // Post new data only when it has changed
           if(consumerQueue!=null) consumerQueue.offer(data);
      }
   }
}

public class Consumer extends Thread{
   private BlockingQueue<E> consumerQueue = new BlockingQueue<E>();
   public Consumer (Producer val){
      val.setConsumerQueue(consumerQueue);
   }
   public void run(){
      while(!interrupted()){
           data = consumerQueue.take();// block until there is data from producer
           if(data !=null) processData(data);
      }
   }
}
公共类通知程序扩展线程{
private BlockingQueue consumerQueue=null;
public setConsumerQueue(阻塞队列val){
consumerQueue=val;
}
//从套接字接收数据的主要方法。。。
公开募捐{
而(!interrupted()){
data=…//这里有新数据
如果(!data.isChanged())继续;
//仅在新数据发生更改时发布新数据
如果(消费者)