2012-03-07 2 views
2

executor를 사용하여 정상적인 다중 스레드와 다중 스레드 간의 성능 차이에 대해 알아 봅니다.Executor를 사용하는 경우와 사용하지 않는 경우의 차이점

다음은 모두에 대한 코드 예제입니다.

(멀티 스레딩) 집행 인 코드없이

: 집행자와

import java.lang.management.ManagementFactory; 
import java.lang.management.MemoryPoolMXBean; 
import java.lang.management.MemoryUsage; 
import java.lang.management.ThreadMXBean; 
import java.util.List; 

public class Demo1 { 
public static void main(String arg[]) { 
    Demo1 demo = new Demo1(); 
    Thread t5 = new Thread(new Runnable() { 
     public void run() { 
       int count=0; 
       // Thread.State; 
       // System.out.println("ClientMsgReceiver started-----"); 
       Demo1.ChildDemo obj = new Demo1.ChildDemo(); 
       while(true) { 

       // System.out.println("Threadcount is"+Thread); 
       // System.out.println("count is"+(count++)); 
       Thread t=new Thread(obj); 
       t.start(); 
       ThreadMXBean tb = ManagementFactory.getThreadMXBean(); 
       List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans(); 
       for (MemoryPoolMXBean pool : pools) { 
        MemoryUsage peak = pool.getPeakUsage(); 
        System.out.format("Peak %s memory used: %,d%n", 
          pool.getName(), peak.getUsed()); 
        System.out.format("Peak %s memory reserved: %,d%n", 
          pool.getName(), peak.getCommitted()); 
       } 

       System.out.println("Current Thread Count"+ tb.getThreadCount()); 
       System.out.println("Peak Thread Count"+ tb.getPeakThreadCount()); 
       System.out.println("Current_Thread_Cpu_Time " 
         + tb.getCurrentThreadCpuTime()); 
       System.out.println("Daemon Thread Count" +tb.getDaemonThreadCount()); 
     } 
     // ChatLogin = new ChatLogin(); 
    } 
    }); 
    t5.start(); 
} 

static class ChildDemo implements Runnable { 
    public void run() { 
     try { 
     // System.out.println("Thread Started with custom Run method"); 
      Thread.sleep(100000); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     finally { 
      System.out.println("A" +Thread.activeCount()); 
     } 
    } 
    } 
} 

(멀티 스레딩) :

import java.lang.management.ManagementFactory; 
import java.lang.management.MemoryPoolMXBean; 
import java.lang.management.MemoryUsage; 
import java.lang.management.ThreadMXBean; 
import java.util.List; 
import java.util.concurrent.ArrayBlockingQueue; 
import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.ThreadPoolExecutor; 
import java.util.concurrent.TimeUnit; 

public class Executor_Demo { 
public static void main(String arg[]) { 
    BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(10); 
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
      10, 100, 10, TimeUnit.MICROSECONDS, queue); 
    Executor_Demo demo = new Executor_Demo(); 

    executor.execute(new Runnable() { 
     public void run() { 
      int count=0; 
      // System.out.println("ClientMsgReceiver started-----"); 
      Executor_Demo.Demo demo2 = new Executor_Demo.Demo(); 
      BlockingQueue<Runnable> queue1 = new ArrayBlockingQueue<Runnable>(1000); 
      ThreadPoolExecutor executor1 = new ThreadPoolExecutor(
        1000, 10000, 10, TimeUnit.MICROSECONDS, queue1); 

      while(true) { 
      // System.out.println("Threadcount is"+Thread); 
      // System.out.println("count is"+(count++)); 
      Runnable command= new Demo(); 
      // executor1.execute(command); 
      executor1.submit(command);   
      // Thread t=new Thread(demo2); 
      // t.start(); 
      ThreadMXBean tb = ManagementFactory.getThreadMXBean(); 
      /* try { 
        executor1.awaitTermination(100, TimeUnit.MICROSECONDS); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } */ 
       List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans(); 
       for (MemoryPoolMXBean pool : pools) { 
       MemoryUsage peak = pool.getPeakUsage(); 
       System.out.format("Peak %s memory used: %,d%n", 
          pool.getName(), peak.getUsed()); 
       System.out.format("Peak %s memory reserved: %,d%n", 
          pool.getName(), peak.getCommitted()); 
      } 
       System.out.println("daemon threads"+tb.getDaemonThreadCount()); 
       System.out.println("All threads"+tb.getAllThreadIds()); 
       System.out.println("current thread CPU time " 
         + tb.getCurrentThreadCpuTime()); 
       System.out.println("current thread user time " 
         + tb.getCurrentThreadUserTime()); 
       System.out.println("Total started thread count " 
         + tb.getTotalStartedThreadCount()); 
       System.out.println("Current Thread Count"+ tb.getThreadCount()); 
       System.out.println("Peak Thread Count"+ tb.getPeakThreadCount()); 
       System.out.println("Current_Thread_Cpu_Time " 
         + tb.getCurrentThreadCpuTime()); 
       System.out.println("Daemon Thread Count" 
         + tb.getDaemonThreadCount()); 
       // executor1.shutdown(); 
      } 
      //ChatLogin = new ChatLogin(); 
      } 
    }); 
    executor.shutdown(); 
} 

static class Demo implements Runnable { 
    public void run() { 
     try { 
     // System.out.println("Thread Started with custom Run method"); 
     Thread.sleep(100000); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     finally { 
     System.out.println("A" +Thread.activeCount()); 
     } 
    } 
    } 
} 

샘플 출력

내가 두 프로그램을 실행 Output, 그것은 집행자가 보통 멀티보다 비싸다는 것이 밝혀졌습니다. 스레딩. 이게 왜 그렇게?

그리고 이것, 정확히 Executor의 사용은 무엇입니까? 실행 프로그램을 사용하여 스레드 풀을 관리합니다.

executor가 정상적인 멀티 스레딩보다 더 나은 결과를 기대했을 것입니다.

기본적으로 멀티 스레딩을 사용하는 소켓 프로그래밍을 사용하는 수백만의 클라이언트를 처리해야하므로이 작업을 수행하고 있습니다.

어떤 제안 사항이 도움이 될 것입니다.

+0

"수백만의 고객"이 한 번에 모두? 얼마나 많은 동시 * 연결을 유지해야합니까? 유지해야하는 수백만 개의 동시 연결 수는 –

+0

입니다. – Java

+1

처음에는 하나의 머신에서이 작업을 시도하지 않았으며, 실행 프로그램의 유무에 관계없이 클라이언트 당 스레드를 사용하여이를 수행 할 생각조차하지 않았습니다. 우선 살펴볼 필요가있는 것은 비동기 IO입니다 ... 어느 시점에서 아주 적은 수의 스레드로 작업 할 수 있습니다. 또한 벤치마킹을 수행 할 때 좀 더 현실적인 테스트를 선택하게 될 것입니다. 실제 코드에서는 반복적으로 루프를 돌리는 것이 아니라 끊임없이 잠들지 않는 스레드를 추가하는 것입니다. –

답변

2

무언가가 어떻게 변하는 지 보려면 모니터링 비용을 최소한으로 유지하려고 노력해야하며 작은 숫자와 큰 숫자를 비교할 것입니다.

public class Executor_Demo { 
    public static void main(String... arg) throws ExecutionException, InterruptedException { 
     int nThreads = 5100; 
     ExecutorService executor = Executors.newFixedThreadPool(nThreads, new DaemonThreadFactory()); 

     List<Future<Results>> futures = new ArrayList<Future<Results>>(); 
     for (int i = 0; i < nThreads; i++) { 
      futures.add(executor.submit(new BackgroundCallable())); 
     } 
     Results result = new Results(); 
     for (Future<Results> future : futures) { 
      result.merge(future.get()); 
     } 
     executor.shutdown(); 

     result.print(System.out); 

    } 

    static class Results { 
     private long cpuTime; 
     private long userTime; 

     Results() { 
      final ThreadMXBean tb = ManagementFactory.getThreadMXBean(); 
      cpuTime = tb.getCurrentThreadCpuTime(); 
      userTime = tb.getCurrentThreadUserTime(); 
     } 


     public void merge(Results results) { 
      cpuTime += results.cpuTime; 
      userTime += results.userTime; 
     } 

     public void print(PrintStream out) { 
      ThreadMXBean tb = ManagementFactory.getThreadMXBean(); 

      List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans(); 
      for (int i = 0, poolsSize = pools.size(); i < poolsSize; i++) { 
       MemoryPoolMXBean pool = pools.get(i); 
       MemoryUsage peak = pool.getPeakUsage(); 
       out.format("Peak %s memory used:\t%,d%n", pool.getName(), peak.getUsed()); 
       out.format("Peak %s memory reserved:\t%,d%n", pool.getName(), peak.getCommitted()); 
      } 

      out.println("Total thread CPU time\t" + cpuTime); 
      out.println("Total thread user time\t" + userTime); 
      out.println("Total started thread count\t" + tb.getTotalStartedThreadCount()); 
      out.println("Current Thread Count\t" + tb.getThreadCount()); 
      out.println("Peak Thread Count\t" + tb.getPeakThreadCount()); 
      out.println("Daemon Thread Count\t" + tb.getDaemonThreadCount()); 
     } 
    } 

    static class DaemonThreadFactory implements ThreadFactory { 
     @Override 
     public Thread newThread(Runnable r) { 
      Thread t = new Thread(r); 
      t.setDaemon(true); 
      return t; 
     } 
    } 

    static class BackgroundCallable implements Callable<Results> { 
     @Override 
     public Results call() throws Exception { 
      Thread.sleep(100); 
      return new Results(); 
     } 
    } 
} 

100 threads 
Peak Code Cache memory used: 386,880 
Peak Code Cache memory reserved: 2,555,904 
Peak PS Eden Space memory used: 41,280,984 
Peak PS Eden Space memory reserved: 50,331,648 
Peak PS Survivor Space memory used: 0 
Peak PS Survivor Space memory reserved: 8,388,608 
Peak PS Old Gen memory used: 0 
Peak PS Old Gen memory reserved: 192,675,840 
Peak PS Perm Gen memory used: 3,719,616 
Peak PS Perm Gen memory reserved: 21,757,952 
Total thread CPU time 20000000 
Total thread user time 20000000 
Total started thread count 105 
Current Thread Count 93 
Peak Thread Count 105 
Daemon Thread Count 92 

5100 threads 
Peak Code Cache memory used: 425,728 
Peak Code Cache memory reserved: 2,555,904 
Peak PS Eden Space memory used: 59,244,544 
Peak PS Eden Space memory reserved: 59,244,544 
Peak PS Survivor Space memory used: 2,949,152 
Peak PS Survivor Space memory reserved: 8,388,608 
Peak PS Old Gen memory used: 3,076,400 
Peak PS Old Gen memory reserved: 192,675,840 
Peak PS Perm Gen memory used: 3,787,096 
Peak PS Perm Gen memory reserved: 21,757,952 
Total thread CPU time 810000000 
Total thread user time 150000000 
Total started thread count 5105 
Current Thread Count 5105 
Peak Thread Count 5105 
Daemon Thread Count 5104 

주요 증가는 이전 세대의 증가 2 ~ 3 MB 이상의 스레드 당 6킬로바이트을 사용하는 것입니다 (이 크기 임시 메모리 공간이 증가 제한) -XX:MaxNewSize=64m 테스트 할 때. CPU는 스레드 당 956ms 또는 약 0.2ms 씩 사용됩니다. 첫 번째 예에서


, 당신은 두 번째에 당신이 1000

수행중인 출력은 대부분의 작업을 것으로 보인다

를 만드는, 하나 개의 스레드를 만드는 당신은 훨씬 더 출력이 두 번째 경우는 첫 번째 경우보다

테스트 및 모니터링이 모니터/측정하려는 것보다 훨씬 가볍습니다.

+0

+1하지만 명확히하기 : 벤치 마크가 유효하지 않음 –

+0

@ JimGarrison 벤치 마크는 모니터링의 두 번째 방법이 첫 번째 방법보다 느리다는 것을 나타냅니다. 당신이 무엇을 측정하고 있는지 조심해야합니다. ;) –

+0

두 번째 경우에는 1000 개의 스레드가 생성되지 않습니다. 이미 1000 개의 스레드 풀을 만들었습니다. exector.submit()'은 executor 스레드 풀에서 스레드 중 하나에 작업을 제출합니다. '@ PeterLawrey' 내가 잘못하면 나를 바로 잡습니다. – Java

2

각 스레드는 스택을 위해 256K에서 1M까지의 메모리를 사용합니다. 수동으로 스택 크기를 설정할 수 있지만 128K 미만으로 설정하는 것은 위험합니다. 따라서 2G 메모리를 사용하고 스레드에 1/2을 쓸 여유가 있다면 8K 스레드를 초과하지 않아야합니다. 정상적인 멀티 스레딩을 사용하십시오 (각 Runnable에는 자체 스택이 있습니다). 각 Runnable에 대해 너무 많은 메모리를 사용하지 않거나 사용할 수없는 경우 Executor를 사용하십시오. 스레드 풀 크기를 프로세서 수 (Runtime.availableProcessors())로 설정하거나 여러 번 더 설정합니다. 주된 문제가 발생합니다. Thread.sleep()을 만들거나 다른 방법으로는 스레드를 차단할 수 없다는 것입니다 (예 : 사용자 응답 대기). 이러한 차단으로 인해 스레드가 효과적으로 서비스를 제외하기 때문입니다.따라서 제한된 크기의 스레드 풀을 사용하면 소위 "스레드 부족"이 발생하여 사실상 교착 상태가됩니다. 스레드 풀이 무제한 크기이면 정상적인 멀티 스레딩으로 돌아가고 곧 메모리가 부족합니다.

치료법은 비동기 작업, 즉 콜백으로 일부 요청을 설정하고 run() 메소드를 종료하는 것입니다. 콜백은, Executor.execute (Runnable)를 사용해, Runnable 객체 (어쩌면 같은 것)의 실행을 개시하거나, 메소드 runnable.run() 자체를 실행할 수 있습니다.

Java 7 (nio2)에서 비동기 입출력 조작이 가능하지만 몇 백 개가 넘는 네트워크 연결을 제공하지 못했습니다. 서비스 네트워크 연결의 경우 비동기 네트워크 라이브러리 (예 : Apache Netty)를 사용할 수 있습니다.

콜백을 구성하고 실행 파일을 실행하려면 정교한 동기화가 필요할 수 있습니다. 보다 쉽게 ​​작업을 수행하려면 Actor 모델 (http://en.wikipedia.org/wiki/Actor_model)을 사용하십시오. Actor는 입력 메시지가 도착할 때마다 실행되는 Runnable입니다. 수많은 Java Actor 라이브러리가 존재합니다 (예 : https://github.com/rfqu/df4j).

+0

Executor가 적은 시간과 메모리 사용량으로 사용해야하는 경우 대답에 따라 executor가 일반적인 threading example.example 샘플 이미지보다 executor 예제에서 더 많은 시간과 메모리를 사용하는 이유는 무엇입니까? – Java

+0

나는 집행자가 더 적은 시간을 사용해야한다고 결코 말하지 않았다. 나는 executor가 쓰레드를 적게 (그리고 메모리를 적게) 사용할 수 있다고 말했지만 예제에서는 executor 예제에서 많은 수의 쓰레드를 사용한다는 것을 보여 주므로 예제가 잘못 설계되었다. –

관련 문제