0

我能解释一下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;
}
}

从线程调用非同步方法 returnBigSpikes() 是否安全?

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

如果我使方法同步会发生什么?会不会将应用程序的 CPU 性能减慢到 1?如果是这样,我怎样才能正确设计它并保持性能?

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

非常感谢您提前。

4

6 回答 6

3

如果在所有线程开始之前填充,您可以通过将其保存为不可修改的 mapallSpikes来确保以后不会更改它。

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

于 2013-03-21T14:52:35.777 回答
1

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

我建议以下 UnmodifiableData:

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

}

于 2013-03-22T10:10:26.027 回答
1

一般来说,如果你有一堆线程,你可以保证只有一个线程会修改资源,其余的只会读取该资源,那么对该资源的访问不需要同步。在您的示例中,每次调用 returnBigSpikes() 方法时,它都会创建 bigSpikes 哈希图的新本地副本,因此尽管您正在创建哈希图,但它对于每个线程都是唯一的,因此不会出现同步问题。

于 2013-03-21T14:59:02.910 回答
0

你的计划应该可以正常工作。你不需要synchronize读,只需要写。

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

于 2013-03-21T14:51:18.843 回答
0

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

于 2013-03-21T14:52:08.107 回答
0

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

于 2013-03-21T14:52:35.843 回答