이 글은 주로 Java 스레드 풀의 사용 및 원리에 대한 관련 정보를 소개합니다. 관심 있는 친구들이 참고할 수 있습니다.
스레드 풀이란 무엇인가요?
Java를 사용하면 새 스레드를 쉽게 생성할 수 있고 운영 체제에서도 스레드를 생성하는 데 비용이 많이 듭니다. 따라서 스레드의 재사용을 기반으로 스레드 풀의 개념을 제안합니다. 스레드 풀을 사용하여 작업을 실행한 후 스레드는 일정 시간 동안 존재합니다(사용자는 생존 시간을 설정할 수 있음). 나중에 설명할 유휴 스레드) 소개) 새 작업이 오면 유휴 스레드가 직접 재사용되므로 스레드 생성 및 삭제 손실이 제거됩니다. 물론 유휴 스레드도 리소스 낭비가 되지만(유휴 스레드만 생존 시간이 제한됨) 스레드를 자주 생성하고 파괴하는 것보다는 훨씬 낫습니다.
다음은 제 테스트 코드입니다
/* * @TODO 线程池测试 */ @Test public void threadPool(){ /*java提供的统计线程运行数,一开始设置其值为50000,每一个线程任务执行完 * 调用CountDownLatch#coutDown()方法(其实就是自减1) * 当所有的线程都执行完其值就为0 */ CountDownLatch count = new CountDownLatch(50000); long start = System.currentTimeMillis(); Executor pool = Executors.newFixedThreadPool(10);//开启线程池最多会创建10个线程 for(int i=0;i<50000;i++){ pool.execute(new Runnable() { @Override public void run() { System.out.println("hello"); count.countDown(); } }); } while(count.getCount()!=0){//堵塞等待5w个线程运行完毕 } long end = System.currentTimeMillis(); System.out.println("50个线程都执行完了,共用时:"+(end-start)+"ms"); } /** *@TODO 手动创建线程测试 */ @Test public void thread(){ CountDownLatch count = new CountDownLatch(50000); long start = System.currentTimeMillis(); for(int i=0;i<50000;i++){ Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("hello"); count.countDown(); } }); thread.start(); } while(count.getCount()!=0){//堵塞等待5w个线程运行完毕 } long end = System.currentTimeMillis(); System.out.println("50000个线程都执行完了,共用时:"+(end-start)+"ms"); }
스레드 풀에서 5w 스레드로 실행하는 데 약 400ms가 걸리고, 스레드 풀을 사용하지 않고 실행하는 데 약 4350ms가 걸립니다. (독자 분들이 테스트해 볼 수 있습니다. 그 자체이지만 컴퓨터 구성이 다르기 때문에 나오는 데이터도 다르지만 스레드 풀을 사용하는 것이 스레드를 생성하는 것보다 확실히 빠릅니다.
Java는 스레드 풀을 어떻게 사용하나요?
스레드 풀은 위의 테스트 코드에서 사용되었으며, 아래에서 정식으로 소개하겠습니다.
모든 Java 스레드 풀의 최상위 수준은 모든 작업을 수행하는 데 사용되는 단 하나의 실행 메서드가 있는 Executor 인터페이스입니다. Java는 Executor에서 상속하고 메서드를 확장하는 ExecutorService 인터페이스도 제공합니다. ExecutorService를 구현하는 AbstractExecutorService 클래스와 위의 추상 클래스를 상속한 ThreadPoolExecutor가 바로 이 클래스의 인스턴스입니다.
위에서 사용한 Executors는 다양한 비즈니스의 스레드 풀 매개변수를 캡슐화하고 새로운 작업을 수행하는 구문 설탕입니다.
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
위는 Executors.newFixedThreadPool(10)의 소스코드이다.
다음은 ThreadPoolExecutor 생성 메소드의 각 매개변수의 의미에 대해 이야기해 보겠습니다.
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
위의 시공 방법이 가장 완벽합니다.
아래에서는 소스 코드를 기반으로 일부 매개변수의 의미를 설명하므로 더욱 설득력이 있을 것입니다.
다음은 위 인터페이스에서 호출되는 실제 Executor인 ThreadPoolExecutor#execute 메소드입니다.
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); } else if (!addWorker(command, false)) reject(command); }
ctl은 원자문에 대한 CAS 연산을 제공하는 클래스인 AtomicInteger 인스턴스입니다. 이는 스레드 풀에서 현재 실행 중인 스레드 수에 -2^29를 더한 값을 기록하는 데 사용되며 workCountOf 메서드는 해당 절대값을 가져옵니다. (구현 방법은 소스 코드에서 확인할 수 있습니다.) corePoolSize보다 작으면 addWorker 메서드가 호출됩니다(새 Worker를 생성하는 데 사용됩니다. Worker는 Thread를 생성하므로 스레드 생성 과정에서 addWorkd는 corePoolSize를 따릅니다. 또는 maxnumPoolSize의 값 비교(true가 전달되면 corePoolSize를 기준으로 비교되고 false는 maxnumPoolSize를 기준으로 비교됩니다. 값보다 크거나 같으면 생성이 실패합니다.) 현재 실행중인 쓰레드의 개수가 corePoolSize보다 작으면 생성되어 성공적으로 생성되는 것을 볼 수 있다.
스레드 풀에서의 스레드 생성 및 거부 전략
corePoolSize, maxnumPoolSize 및 BlockingQueue는 함께 논의되어야 합니다스레드 풀에서 실행 중인 스레드가 corePoolSize보다 작으면 새 스레드 작업은 항상 실행을 위해 새 스레드를 생성합니다. 이 스레드가 corePoolSize보다 크면 해당 작업은 BlockingQueue를 전달하는 경우 대기 대기열에 추가됩니다. in은 제한되지 않은 대기열(LinkedBlockingQueue)입니다. 이는 "무한히 많은" 작업을 저장할 수 있는 대기열입니다. 모든 작업은 항상 대기열에 성공적으로 추가됩니다. 이는 최대 스레드 수와도 관련이 없습니다. 스레드 풀에는 corePoolSize가 있지만 제한된 대기열(ArrayBlockingQueue, 동기 대기열)을 전달하는 경우 대기열이 가득 차고 스레드 수가 maxmunPoolSize보다 적으면 스레드 수가 maxnumPoolSize보다 커질 때까지 새 스레드가 생성됩니다. 스레드 수가 maxnumPoolSize보다 크면 작업 조인이 스레드 풀에 의해 거부됩니다.
RejectedExecutionHandler 거부 전략 java는 4개의 AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy, DiscardPolicy를 구현합니다. 사용자는 이 인터페이스를 구현하여 자신의 거부 전략을 직접 구현할 수도 있고, 두 번째는 처리를 시도할 수 있습니다. 작업은 직접 실행됩니다. 세 번째 작업은 대기열에서 가장 오래된 작업을 취소합니다.
위 내용은 Java에서 스레드 풀의 사용 및 원리에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!