【问题标题】:Thread safe : Instance of class线程安全:类的实例
【发布时间】:2015-11-01 06:47:22
【问题描述】:

下面是来自Java concurrency in Pratice 的第一个代码sn-p。我不明白这个类如何不是线程安全的?不是每次线程需要调用getNext()方法时,都会先创建这个类的实例吗?两个线程可以共享这个类的同一个实例吗(barring explicit share)?

@NotThreadSafe
public class UnsafeSequence {
private int value;
/** Returns a unique value. */
public int getNext() {
return value++;
}
 }

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

    我不明白这个类为什么不是线程安全的?

    您现在可能已经回答了这个问题,但为了完整起见,我将其包含在此处。)

    问题在于value++ 语句。这是一个多部分的声明。

    1. value 的值被读取。
    2. 读取值加一。
    3. 新值存储在value

    这个序列可以被另一个线程混合。假设value5。如果两个线程调用getNext()(在同一个实例上),你会期望value 在它们完成后是7。但是,如果两个线程在任何线程执行步骤 3 之前都完成了步骤 1 和 2,它们都会将值 6 写入 value

    • 线程 1 执行步骤 1 - 读取 5
    • 线程 2 执行步骤 1 - 读取 5
    • 线程 2 执行步骤 2 - 将 5 增加 1
    • 线程 2 执行步骤 3 - 保存值 6
    • 线程 1 执行步骤 2 - 将 5 增加 1
    • 线程 1 执行步骤 3 - 保存值 6

    不会每次线程需要调用 getNext() 方法时,它都会 首先创建这个类的实例?

    不是每次。那将是new UnsafeSequence().getNext() 一遍又一遍,这是没有意义的。但也许这不是你的意思。每个线程都可以拥有自己的类实例并在其上调用getNext()。这种情况下没有问题。

    两个线程可以共享这个类的同一个实例吗(除非显式 分享)?

    不,必须以某种方式共享实例。但它可以在你不知情的情况下被分享。例如,某些类可能有一个static 方法,它返回一个UnsafeSequence 的实例。您不会知道每次返回的实例是否相同,或者每次调用是否都创建了一个新实例。 除非这在某处有记录。


    API 文档中关于类是否是线程安全的讨论是指实例在线程之间共享的情况。如果实例未共享,则可以在多线程应用程序中使用它,只要它可用并且仅由一个线程使用。

    【讨论】:

      【解决方案2】:

      两个线程可以共享这个类的同一个实例吗(除非显式共享)?

      我认为这是你误解的根源。

      基本上,如果两个线程都使用对给定实例的引用,则它们共享该实例。他们如何设法获得参考并不重要。 “显式”和其他共享引用的方式之间没有真正的区别。

      该示例的基本假设是两个线程>确实

      (如果他们没有,或者确实只有一个线程,那么类的线程安全或其他方面都不会成为问题。使用 Goetz 等人的术语,“线程受限”对象无关紧要。)

      【讨论】:

        【解决方案3】:

        是的,显然 2 个或更多线程可以同时访问同一个对象。那是发生竞争条件的时候,即当多个线程争用共享资源时(在您的情况下,UnsafeSequence 的同一对象)。

        EG:

        UnsafeSequence seq = new UnsafeSequence();
        // both thread instances share same object
        Thread t1 = new MyThread(seq);
        Thread t2 = new MyThread(seq);
        t1.start();
        t2.start();
        
        // run method for above `MyThread` class:
        public void run() {
           while(some condition) {
            // some work
            System.out.println(seq.getNext()); // sequence is unpredictable
           }
        }
        

        让不同的线程访问不同的对象实例,不会有线程安全问题。

        【讨论】:

        • 我看不到明显的地方。愿意在问题的背景下解释吗?
        • 谢谢。我理解“共享”时,请参阅我的问题的最后一句话。
        • 对不起,我没有得到最后一部分。能否请您详细说明。
        【解决方案4】:

        由于Java允许多线程语言(其中多个线程并行运行以完成程序执行) 在多线程环境下,java对象的同步或java类的同步变得极为重要。

        同步是一项功能,它允许一次只有一个线程可以访问资源。它适用于锁定的概念。

          ThreadDemo T1 = new ThreadDemo(safeSeq);
          ThreadDemo T2 = new ThreadDemo(safeSeq);
        

        在上面的代码中,我们在这里为两个线程提供了单独的锁

        public class UnsafeSequence {
        private int value;
        /** Returns a unique value. */
        public int getNext() {
        return value++;
           }
         }
        
        
        class ThreadDemo extends Thread {
        
        UnsafeSequence  safeSeq;
        
         public void run() {
        
         some code here
        
         safeSeq.getNext();
        
         }
        
        }
        
        public class TestThread {
        public static void main(String args[]) {
        
          UnsafeSequence safeSeq = new UnsafeSequence();
        
          ThreadDemo T1 = new ThreadDemo(safeSeq);
          ThreadDemo T2 = new ThreadDemo(safeSeq);
        
          T1.start();
          T2.start();
        
             }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-01-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多