线程访问Java中的非同步方法

时间:2013-03-21 14:46:27

标签: java multithreading thread-safety thread-synchronization

我可以要求解释一下线程和同步在Java中是如何工作的吗?

我想写一个高性能的应用程序。在这个应用程序中,我将文件中的数据读入一些嵌套类,这些类基本上是围绕HashMap的坚果壳。

数据读取完成后,我启动需要浏览数据并对其执行不同检查的线程。但是,线程永远不会更改数据!

如果我能保证(或者至少试图保证;)我的线程永远不会更改数据,我可以使用它们调用包含数据的对象的非同步方法吗?

如果多个线程访问非同步方法,它不会更改任何类字段,但有一些内部变量,是否安全?

仿制例子:

public class Data{
// this hash map is filled before I start threads
protected Map<Integer, Spike> allSpikes = new HashMap<Integer, Spike>();

public HashMap returnBigSpikes(){
     Map<Integer, Spike> bigSpikes = new HashMap<Integer, Spike>();

     for (Integer i: allSpikes.keySet()){
         if (allSpikes.get(i).spikeSize > 100){
         bigSpikes.put(i,allSpikes.get(i));
         }
     }

     return bigSpikes;
}
}

从线程调用NON-synchronized方法returnBigSpikes()是否安全?

我现在明白这样的用例可能非常危险,因为它很难控制,数据(例如,返回的bigSpikes)将不会被修改。但我已经像这样实现并测试了它,想知道我现在是否可以使用我的应用程序的结果,并在以后更改架构......

如果我使方法同步会怎样?将应用程序的速度降低到1 CPU性能?如果是这样,我如何正确设计并保持性能?

(我将大约20-40 Gb的数据(日志消息)读入主内存,然后运行线程,这需要通过所有数据来查找其中的一些相关性;每个线程只成为消息的一部分但是对于分析,线程应该将来自其部分的每条消息与来自数据的许多其他消息进行比较;这就是为什么我首先决定允许线程在没有同步的情况下读取数据的原因。

非常感谢你。

6 个答案:

答案 0 :(得分:3)

如果在所有线程开始之前填充allSpikes,您可以确保稍后通过将其保存为unmodifiable map来更改它。

假设Spike是不可变的,那么您的方法可以非常安全地同时使用。

答案 1 :(得分:1)

通常,如果您有一堆线程,您可以保证只有一个线程将修改资源,而其余线程只读取该资源,则不需要同步对该资源的访问。在您的示例中,每次调用returnBigSpikes()方法时,它都会创建一个bigSpikes hashmap的新本地副本,因此虽然您创建的是一个hashmap,但它对每个线程都是唯一的,因此没有同步问题。

答案 2 :(得分:1)

只要任何实际上不可变的东西(例如使用final关键字)并使用不可修改的Map一切都很好。

我建议使用以下UnmodifiableData:

public class UnmodifiableData {
     final Map<Integer,Spike>  bigSpikes;
     public UnmodifiableData(Map<Integer,Spike> bigSpikes) {
         this.bigSpikes = Collections.unmodifiableMap(new HashMap<>(bigSpikes));
     }
     ....

}

答案 3 :(得分:0)

你的计划应该可以正常运作。您不需要synchronize次读取,只需要写入。

但是,如果您希望将来缓存bigSpikes以便所有线程获得相同的映射,那么您需要更加小心同步。

答案 4 :(得分:0)

如果您使用ConcurrentHashMap,它将为您执行所有同步工作。它比围绕普通的HashMap进行同步更好。

答案 5 :(得分:0)

由于在启动线程之前初始化allSpikes,因此是安全的。并发问题仅在线程写入资源而其他人从中读取时出现。