[英]Multi-threading program taking longer than single thread (Java)
我必須編寫一個程序來讀取文件中的所有單詞並確定每個單詞的使用次數。 我的任務是使用多線程來加快運行時間,但單線程程序比多線程程序運行得更快。 我已經嘗試研究解決方案,但很多解釋只會讓我更加困惑。 我對使用線程非常陌生,想知道是否有人可以指出我修復代碼的正確方向,以便創建線程的開銷不會導致程序運行速度比單線程慢。
public class Main
{
final static int THREADS = 4;
static HashMap<String, Integer> map = new HashMap<>();
static List<String> file = new ArrayList<String>();
static String filename = "D:\\yes.txt";
static int count;
public static void main(String args[]) throws Exception
{
long startTime = System.nanoTime();
Monitor m = new Monitor();
final Queue<String> dataQueue = new ConcurrentLinkedQueue<>();
try ( Scanner in = new Scanner(new File(filename)))
{
while ( in.hasNext() )
{
dataQueue.add( in.next() );
}
}
catch ( IOException e )
{
e.printStackTrace();
}
Thread T1 = new Thread( new WordCount(m, map, dataQueue ));
Thread T2 = new Thread( new WordCount(m, map, dataQueue ));
Thread T3 = new Thread( new WordCount(m, map, dataQueue ));
Thread T4 = new Thread( new WordCount(m, map, dataQueue ));
T1.start();
T2.start();
T3.start();
T4.start();
//wait for threads to end
try {
T1.join();
T2.join();
T3.join();
T4.join();
} catch ( Exception e) {
System.out.println("Interrupted");
}
Set<String> keys = map.keySet();
for (String key : keys)
{
System.out.println(key);
System.out.println(map.get(key));
}
long endTime = System.nanoTime();
System.out.println("Thread Took "+((endTime - startTime)/100000) + " ms");
}
}
public class WordCount implements Runnable
{
private Monitor m;
private Queue<String> dataQueue;
private HashMap<String, Integer> map;
public WordCount(Monitor m, HashMap<String, Integer> map,Queue<String> dataQueue)
{
this.m = m;
this.dataQueue = dataQueue;
this.map = map;
}
@Override public void run()
{
while ( !dataQueue.isEmpty() )
{
String line = dataQueue.poll();
m.keySet(map, line);
}
}
}
public class Monitor
{
public synchronized void keySet(HashMap<String, Integer> map, String word)
{
String[] words = filterIllegalTokens(word );
String[] lowerCaseWords = mapToLowerCase( words );
for ( String s : lowerCaseWords ) {
if (map.containsKey(s))
{
map.put(s, map.get(s) + 1);
}
else
{
map.put(s, 1);
}
}
}
public String[] filterIllegalTokens(String words)
{
List<String> filteredList = new ArrayList<>();
if ( words.matches( "[a-zA-Z]+" ) ) {
filteredList.add( words );
}
return filteredList.toArray( new String[filteredList.size()] );
}
public String[] mapToLowerCase( String[] words )
{
String[] filteredList = new String[words.length];
for ( int i = 0; i < words.length; i++ ) {
filteredList[i] = words[i].toLowerCase();
}
return filteredList;
}
}
這是我的三個班級。 任何提示或建議?
根據經驗,操作系統需要一個 CPU 內核,其他內核可用於程序。 因此,您至少需要 5 個 CPU 內核才能獲得最佳性能。
創建這幾個線程的開銷並不重要。 當您在幾毫秒內啟動數十個線程時,這將變得更加相關。
您代碼中的主要問題是您訪問共享內存區域中的數據占總時間的 90%。 在這種情況下,我們討論的是ConcurrentLinkedQueue
和同步的Monitor.keySet()
方法。 當一個線程訪問這些對象時,其他 3 個線程必須等待。 當您長時間運行程序時,您可能會注意到只使用了總 CPU 能力的一小部分。
為了提高性能,我建議在啟動線程之前將作業隊列拆分為 4 個數據包,這樣每個線程就可以處理自己的數據包而無需等待其他線程。 此外,每個線程都應將其結果收集在一個單獨的容器中。 然后最后(線程完成后),您可以合並四個結果。
如果您的工作線程更復雜,那么您的問題就不那么難了。 例如,如果對容器的訪問只需要總時間的 10%(而某些計算需要 90%),那么線程同步的開銷也會少得多 - 相對於總執行時間。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.