2016-12-16 8 views
2

이 코드는 매우 느리게 실행됩니다. 스레드를 버렸고 거의 한 스레드가 동시에 실행되지만 ExecutorServiceForkJoinPool으로 변경하면 코드가 매우 빠르게 실행됩니다. 스레드가 기다리고있는 이유는이 코드에서 어떤 실제 결론을 도출 꽤 어렵다 ... 내 컴퓨터는 8 개의 코어를 가지고,스레드는 ThreadPoolExecutor에서 대기 중입니다

enter image description here

public class Tests { 

    public static void main(String[] args) throws InterruptedException { 

     int NUM_OF_THREADS = 8; 
     int NUM_OF_INCREMENTS = 100_000_000; 
     //ExecutorService service = Executors.newWorkStealingPool(); 
     ExecutorService service = Executors.newFixedThreadPool(NUM_OF_THREADS); 
     final Counter counter = new StupidCounter(); 

     long before = System.currentTimeMillis(); 
     for (int i = 0; i < NUM_OF_INCREMENTS; i++) { 
      service.submit(newCounterClient(counter, i)); 
     } 
     service.shutdown(); 
     service.awaitTermination(1, TimeUnit.MINUTES); 
     long end = System.currentTimeMillis(); 
     System.out.println(end - before); 
     System.out.println(counter.getCounter()); 
    } 


    static class CounterClient implements Runnable { 
     private Counter counter; 
     private int num; 

     public CounterClient(Counter counter, int num) { 
      this.counter = counter; 
      this.num = num; 
     } 

     @Override 
     public void run() { 
      counter.increment(); 
     } 
    } 

    static interface Counter { 
     void increment(); 

     long getCounter(); 
    } 

    static class StupidCounter implements Counter { 
     long i = 0; 

     @Override 
     public void increment() { 
      i++; 
     } 

     @Override 
     public long getCounter() { 
      return i; 
     } 
    } 

} 

"pool-1-thread-7" #17 prio=5 os_prio=31 tid=0x00007faaa481c000 nid=0x6503 waiting on condition [0x0000700001d6d000] 
    java.lang.Thread.State: WAITING (parking) 
    at sun.misc.Unsafe.park(Native Method) 
    - parking to wait for <0x00000006c006b3d8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) 
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) 
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) 
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:897) 
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222) 
    at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335) 
    at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:439) 
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
    at java.lang.Thread.run(617Thread.java:745) 

답변

0

을 모르는 , 실제로는 을 수행하지 않으므로을 수행하십시오. 실행 프로그램의 내부 큐는 병목 현상이므로, 한 번에 하나의 스레드 만 "작동"하는 것을 볼 수 있습니다. 그것은 실제로 작동하지 않습니다. 대기열에서 다음 작업을 얻고 다른 모든 스레드 (다음 작업도 가져옴)를 대기시킵니다. LinkedBlockingQueue.take()의 기능을 제외하고는 여기에서 실제로 테스트하지 않습니다.

increment() 메서드는 스레드로부터 안전하지 않으므로 기본적으로 나쁜 코드를 테스트하고 결과는 거의 무의미합니다. CounterClient 작업을 실제 작업을 수행하는 데 여러 밀리 초가 걸리는 경우 성능이 다를 경우 훨씬 작은 차이가 나타납니다 (ForkJoinPool).

0

FixedThreadPool을 사용할 때 프로그램이 많은 시간을 소비하기 때문에 대부분 GarbageCollection에 사용됩니다. VM 매개 변수 -Xmx3g을 사용하여 실행을 시도했지만 VM이 메모리 부족을 피하려고 시도하는 동안 여전히 가비지 수집에 걸렸습니다.

메모리 덤프를 사용하여 소비의 근본 원인을 파악하고 싶지만 인스턴스가 카운터에있는 것 같습니다.

어쨌든, 근본 원인은 LinkedBlockingQueue 등를 사용하여이 WorkqueueFixedThreadPool 것 같다. workQueue에 100_000_000 개 요소를 추가하는 for 루프 때문에 매우 드물게 다른 스레드가 그 요소를 취합니다. 그래서 그들은 대부분 카운터 클라이언트이 제출 될 때까지 기다리고 있습니다.

ForkJoinPool이 더 비 차단 방식을 사용하고, 지금까지의 내가 제출 된 행동에 대한 여러 이 Workqueue의 이해한다.

관련 문제