Java 线程多久执行一次?我的观察者模式出错了?
下面是我当前代码的简化版本。我很确定我在语法方面没有做错任何事情,我也找不到我在概念上的错误 这是我试图实现的一种观察者模式。我负担不起从Java.utils.observable继承,因为我的类已经很复杂,并且是从另一个类继承的。 这里有两个部分: 有一个通知程序类实现Runnable:Java 线程多久执行一次?我的观察者模式出错了?,java,multithreading,Java,Multithreading,下面是我当前代码的简化版本。我很确定我在语法方面没有做错任何事情,我也找不到我在概念上的错误 这是我试图实现的一种观察者模式。我负担不起从Java.utils.observable继承,因为我的类已经很复杂,并且是从另一个类继承的。 这里有两个部分: 有一个通知程序类实现Runnable: public class Notifier implements Runnable{ public void run() { while(true) {
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实现,我应该如何设计主题观察者系统
数据
字段在多个线程之间共享,因此必须使用volatile
关键字对其进行标记
private volatile MyDataType data;
这会在读写操作周围形成“内存屏障”,使所有线程都能看到该值。即使通知程序线程正在调用getData()
,如果data
的内存缓存不可用,也会检索出它的值。如果没有内存屏障,数据
值将随机更新或从不更新
正如@JB在评论中提到的,volatile
保护您不被重新分配数据
字段。如果更新当前数据
值中的一个字段,则不会跨越内存屏障,通知程序的内存将不会更新
回顾您的代码,看起来是这样的:
if(data.isChanged()==true)
{
refresh();
}
如果未将数据
分配给新对象,则将数据
设置为易失性
对您没有帮助。你必须:
- 设置某种
易失性布尔脏每当
更新时,代码>字段数据
- 每次在
块中更新或读取同步
数据
数据
字段在多个线程之间共享,因此必须使用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())继续;
//仅在新数据发生更改时发布新数据
如果(消费者)