当前位置: 首页 > 知识库问答 >
问题:

在Java中发生在relation之前的一个具体例子

林星阑
2023-03-14

参考以下链接http://docs.oracle.com/javase/tutorial/essential/concurrency/QandE/answers.html下面的示例表明,不能保证Key语句1在Key语句2之前执行

public class BadThreads {

    static String message;

    private static class CorrectorThread
        extends Thread {

        public void run() {
            try {
                sleep(1000); 
            } catch (InterruptedException e) {}
            // Key statement 1:
            message = "Mares do eat oats."; 
        }
    }

    public static void main(String args[])
        throws InterruptedException {

        (new CorrectorThread()).start();
        message = "Mares do not eat oats.";
        Thread.sleep(2000);
        // Key statement 2:
        System.out.println(message);
    }
}

解决方案指出,有两种方法可以保证对消息的所有更改都对主线程可见:

  • 在主线程中,保留对更正程序实例的引用。然后在引用消息之前在该实例上调用联接
  • 使用同步方法将消息封装在对象中。除非通过这些方法,否则切勿引用消息。

这两种技术都建立了必要的发生前关系,使消息的更改可见。

我理解第一个使用join的解决方案是如何使key statement 1“发生在key statement 2之前”的。但是对于第二种解决方案,我不明白如何使用同步方法(比如getMessage()和setMessage())来建立这种关系。修改后的key语句2(system . out . println(getMessage())在修改后的key语句1 (setMessage("母马确实吃燕麦"))之后执行有什么保证。Key语句2可能在key语句1之前锁定消息,反之亦然,这取决于线程的调度方式。

另外,有没有办法修改代码,让message=“Mares do吃燕麦”在message=“Mares do吃燕麦”之前执行?我能想到的一种方法是保留一个共享状态变量,并在message=“Mares don not吃燕麦”执行后设置它,而message=“Mares do吃燕麦”应该在一个受保护的块中,就像while(! stateVariable){等待();}然后更新消息。对吗?

谢谢。

共有3个答案

阴焱
2023-03-14

作为回答,如该网站所述,它说:< br >

然而,这个结果并不能保证,因为在“Key statement 1”和“Key tatement 2”之间的关系之前并没有发生过。即使“Key statement 1”实际上在“Key statement 2”之前执行,这也是正确的-请记住,在关系涉及可见性而不是顺序之前发生。

然后它说:

有两种方法可以保证对消息的所有更改都对主线程可见:

这并不是说< code >母马吃燕麦。肯定会打印在结果中。!!根据定义,同步保证了可见性和原子性。因此,如果消息是通过对象的synchronized方法设置和获取的,则可以确保如果消息被< code>set方法更改,则< code>get方法在读回消息时肯定会看到此更改的值。所以如果我们把代码改成这样:< br >

class Message
{
    String message ;
    public Message(){}
    public Message(String message)
    {
        this.message = message;
    }
    public synchronized void setMessage(String message)
    {
        this.message = message;
    }
    public synchronized String getMessage()
    {
        return message;
    }
}
public class BadThreads {

    //static String message;
    static Message mess = new Message();
    private static class CorrectorThread extends Thread 
    {
        public void run() 
        {
            try 
            {
                sleep(1000); 
            } catch (InterruptedException e) {}
            // Key statement 1:
            mess.setMessage ("Mares do eat oats."); 
        }
    }

    public static void main(String args[]) throws InterruptedException 
    {
        (new CorrectorThread()).start();
        mess.setMessage("Mares do not eat oats.");
        Thread.sleep(2000);
        // Key statement 2:
        System.out.println(mess.getMessage());
    }
}

如果Key Statement 1Key Statement 2来检索消息的最新更改值。

马弘和
2023-03-14

“发生在之前”并不能保证某个语句会在其他语句之前执行。它保证,如果第一个语句在第二个语句之前执行,(在您的示例中很有可能发生这种情况,因为两个线程同时启动,一个线程Hibernate1秒,而另一个线程Hibernate2秒),第二个线程将看到第一个线程写了什么。

对变量的同步访问保证了对该变量的写入对于随后从另一个线程读取该变量是可见的。为什么?因为这就是Java内存模型的规范所说的,并且因为实现尊重该规范。

还有其他方法可以保证这一点:使变量可变,使其成为AtomicReference,或者存储它并从并发集合中检索它。

长孙泉
2023-03-14

不要将之前发生的事情与任何特定的行动顺序相混淆。这种关系的要点在于,这种排序是存在的,而不是任何特定的排序。在您的示例中,没有排序:一个线程可能永远不会观察到另一个线程的动作,即使您使用了更多的sleep语句并试图读取另一个线程所写的内容。

如果你想协调你的两个动作,不要求助于< code > wait /< code > notify ,因为那些是非常低级和脆弱的机制。使用< code > Java . util . concurrent 中的内容,例如< code>CountDownLatch。

 类似资料:
  • 教程http://tutorials.jenkov.com/java-concurrency/volatile.html说 如果读取/写入最初发生在对易失性变量的写入之前,则不能将从其他变量的读取和写入重新排序为在对易失性变量的写入之后发生。写入挥发性变量之前的读取/写入保证在写入挥发性变量之前“发生”。 什么是“写入挥发性变量之前”?这是否意味着以前的读/写在相同的方法中,我们正在写入易失性变量

  • 问题内容: 同步语句建立事前关联。但是我不确定细节。在http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package- summary.html中, 可以阅读 发生监视器的解锁(同步块或方法退出)-在该监视器的每个后续锁定(同步块或方法入口)之前 我想知道我是否理解正确。因此,请看以下示例。假设有2个线程T1,T2共享类D

  • 我有一个java类来处理多线程订阅服务。通过实现可订阅接口,任务可以提交给服务并定期执行。代码草图如下所示: 因此,该类允许: 订阅新任务 取消订阅现有任务 延迟定期执行的任务 订阅由外部控制线程添加/删除,但延迟仅由内部工作线程引起。例如,如果工作线程从上次执行中没有发现更新,或者例如,如果线程只需要从00.00 - 23.00执行,则可能发生这种情况。 我的问题是工作线程可能调用< code>

  • 问题内容: 我缺少一些基本的理解/理论,我不理解这些函数调用之间的区别: 我在这里想要做到的是获取一家商店的分销商列表(多对多关系),并且他们将每个啤酒分销商列表都整合到一个巨型列表中。 我不知道这是否是执行此操作的最佳方法,但我无法使其正常工作。与第一批方法类似,我不知道是否需要或 更新资料 感谢所有回答!这将是我前进的良好参考。我最大的教训是取回集合与取回查询构建器/关系对象之间的区别。为了将

  • 问题内容: 在这里,我的疑问是,根据实践,如果您使用易失性,安全的发布方式发生Java并发事件(即,一旦该引用对另一个线程可见,则该数据也将可用)。我可以在这里使用它吗?但是,如果正确,则假定线程1现在检查“资源”,并且它为null,因此开始创建对象。当线程1创建对象时,另一个线程即线程2出现并开始检查“资源”的值,线程2发现它为空(假设创建“资源”对象需要花费大量时间,并且由于线程1尚未完成创建

  • 我有一个问题,关于如何通过Java内存模型保证对象是线程安全的。 我读过很多书,说在构造函数中编写同步作用域没有意义,但为什么没有呢?是的,只要构造中的对象不在线程之间共享(不应该共享),除了构造线程之外,没有其他线程可以访问任何同步的(this{…}),因此,无需在构造函数中设置该范围来排除它们。但同步作用域不仅仅是为了排除;它们还用于创建发生在关系之前的事件。JLS。17.4 这是一个示例代码