【问题标题】:Multi threading and shared object多线程和共享对象
【发布时间】:2013-02-07 07:32:09
【问题描述】:

如果我有这样的课程:

class MultiThreadEg {

private Member member;

public Integer aMethod() {
    ..............
    ..............
}

public String aThread() {
    ...............
    member.memberMethod(.....);
    Payment py = member.payment();
    py.processPayment();
    ...........................
}

}

假设 aThread() 是一个新线程,那么,太多线程同时访问共享的 member 对象会导致任何问题(如下访问规则)?

Rule 1 : ONLY reading, no writing to the object(member).
Rule 2 : For all the objects that need some manipulation(writing/modification), a copy of the original object will be created.

例如:在 payment() 方法中,我这样做:

public class Member {

private Payment memPay;

public payment() {
   Payment py = new Payment(this.memPay);//Class's Object copy constructor will be called.
   return py;
}

}

我担心的是,即使我为“写入”创建了对象副本(如在方法 payment() 中),访问 member 对象的线程过多同时会造成一些差异。

事实是什么?这种实现在每种情况下是否可靠(0 个或多个并发访问)?请指教。谢谢。

【问题讨论】:

    标签: java multithreading jakarta-ee shared-memory


    【解决方案1】:

    您可以简单地使用ReentrantReadWriteLock。这样,您可以同时读取多个线程,而不会出现问题,但只允许一个线程修改数据。 Java 会为您处理并发。

     ReadWriteLock rwl = new ReentrantReadWriteLock();
     Lock readLock = rwl.readLock;
     Lock writeLock = rwl.writeLock;
    
     public void read() {
    
        rwl.readLock.lock();
        try {
           // Read as much as you want.
        } finally {
           rwl.readlock.unlock();
        }
     }
    
     public void writeSomething() {
        rwl.writeLock.lock();
        try {
           // Modify anything you want
        } finally {
           rwl.writeLock.unlock();
        }
     }
    

    请注意,您应该在 try 块开始之前 lock(),以确保在开始之前就已经获得了锁。并且,将 unlock() 放在 finally 子句中可以保证,无论 try (early return,) 中发生什么,都会引发异常等),锁将被释放。

    【讨论】:

    • 如果我锁定,其他线程将不得不等待,这可能会导致性能问题。因此,如果只是读取共享对象或在写入的情况下创建对象的新副本并对其进行修改不会导致任何问题,我将继续使用它。否则我将不得不考虑其他事情。
    • @M-D 所以你是说,如果有 20 个线程调用 payment() 方法,那么 memPay() 会有 20 个相同的 Payment 副本吗?这样做的目的是什么?那么,Member 类更像是工厂吗?
    • 是的,会有 20 个相同的副本(或者换句话说,它类似于创建 20 个不同的 Payment 对象)。
    • 它只有一个目的:速度。假设在创建支付对象或成员对象期间执行了许多 CPU 密集型任务,那么我将不得不为所有新线程重复所有这些耗时的步骤,所以我基本上在构造函数中完成所有这些,如果其他人需要修改原始对象,则会创建一个新的已创建对象的副本(需要创建对象时间的1/1000),如果只是读取,将使用原始对象。跨度>
    【解决方案2】:

    如果对 memPay 的更新取决于 memPay 内容(如 memPay.amount+=100),您应该在更新时阻止其他线程的访问。这看起来像:

    mutual exclusion block start
    get copy
    update copy
    publish copy
    mutual exclusion block end
    

    否则,当两个线程同时开始更新 memPay 对象时,可能会丢失更新。

    【讨论】:

    • 不,在更新的情况下,我创建了一个 Payment 的新对象。请参阅 new 方法调用。所以你的意思是如果只是阅读就不会有问题(即使有数千个)?
    • 这取决于您考虑的问题。但一般来说,如果 any 数量的线程只是读取内存,我不会看到任何问题。 Please see the new method call. 当两个线程同时更新同一个对象时,这并不能让你免于麻烦。您可能会丢失更新。例如,运行多个线程,在循环中增加相同的变量:int shared = 0; 在每个线程中:shared++。执行后,shared 的值很可能会小于其顺序更新方案变体(没有第一个变体中的并行更新)。
    • 不,如果我这样做,更新不会有任何问题。因为您更新了一个新对象,一个副本(准确地说,副本和原始对象都有不同的引用,但在我修改副本之前,它们的属性将具有相同的值)。我已经对此进行了测试,但无法以同时有许多访问的方式进行测试。
    • 如果`不能以同时有许多访问的方式进行测试`,你怎么能说No,there will be no issues with updation if I do it that way
    • 我的意思是,当我更新复制的对象时,目前没有问题。并且理论上认为,即使我们使用多个线程进行操作也不会有任何问题,因为它们都有自己的原始副本(不同的对象引用)。但是,我只是怀疑当同时有数十万次访问时可能存在一些问题。这只是我想确定的猜测。
    猜你喜欢
    • 2012-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多