1. Conjunto de threads e comparação de desempenho de threads
No caso de thread único, a velocidade de processamento de tarefas é muito melhor em comparação com o desempenho do pool de threads. O pool de threads pode ser reutilizado internamente. Depois que o thread processa a tarefa, ele pode retornar ao pool de threads para processar outras tarefas, o que reduz bastante a destruição de encadeamentos e o tempo de criação.
Vários pools de encadeamentos que acompanham o java:
ExecutorService executorService = Executors.newCachedThreadPool(); ExecutorService executorService1 = Executors.newFixedThreadPool(100); ExecutorService executorService2 = Executors.newSingleThreadExecutor(); ExecutorService executorService3 = Executors.newScheduledThreadPool(10);Os parâmetros de construção do pool de threads acima são todos criados com ThreadPoolExecutor, mas os parâmetros usados são diferentes.
O método de construção usado por newCachedThreadPool: parâmetros correspondentes (número de threads principais, número máximo de threads, tempo de sobrevivência do thread, unidade de tempo de sobrevivência do thread, fila), newCachedThreadPool tem apenas 0 threads principais e o número de threads temporários pode ser infinito, então sua eficiência de execução é muito alta, SynchronousQueue é um típico modelo produtor-consumidor, o que equivale a uma empresa de terceirização.
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>())O método de construção usado por newCachedThreadPool: o número de threads principais e o número de threads temporários são personalizados. A fila LinkedBlockingQueue é uma fila de bloqueio ilimitada, o que equivale a aguardar o processamento da tarefa indefinidamente, mas a eficiência é muito baixa. Equivale a uma empresa estatal. new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit. MILLISECONDS, new LinkedBlockingQueue<Runnable>())O método de construção usado por newSingleThreadExecutor: o número de threads principais e o número de threads temporários são 1, o que equivale a uma pessoa lidando com inúmeras tarefas. A eficiência é muito rápida quando as tarefas podem ser processadas rapidamente, mas será lenta quando as tarefas precisam ser processadas. Parece muito ineficiente. É equivalente a uma empresa privada, e tudo é feito por uma pessoa. novo FinalizableDelegatedExecutorService (novo ThreadPoolExecutor(1, 1, 0L, TimeUnit. MILLISECONDS, novo LinkedBlockingQueue<Runnable>()))O pool de encadeamentos com maior probabilidade de ter OOM: newCachedThreadPool, que não é uma fila ilimitada, relatará OOM quando atingir o valor máximo de memória
Os três casos anteriores não são recomendados pelas especificações de Ali, mas os três acima podem ser usados em empresas que não conseguem atingir o valor máximo de memória. Ali recomenda o pool de threads personalizado ThreadPoolExecutor
1.1 Comparação de pools de threads na prática
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadDemo {
public static void main(String[] args) {
//定义3个线程池
ExecutorService executorService = Executors.newCachedThreadPool();//很快
ExecutorService executorService1 = Executors.newFixedThreadPool(100);//不快不慢
ExecutorService executorService2 = Executors.newSingleThreadExecutor();//最慢
//定义100个任务给线程池处理对比处理性能
long begin = System.currentTimeMillis();
for(int i=0;i<100;i++){
executorService2.submit(new Task());
}
executorService.shutdown();
long end = System.currentTimeMillis();
System.out.println("处理时间:"+(end-begin)/1000+"秒");
}
}
class Task implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"处理完任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1.2 Combate ThreadPool Executor
/**
* 第31个任务会拒绝任务
*/
public class ThreadDemo {
public static void main(String[] args) {
//定义ThreadPoolExecutor
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 30, 0, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10));
//定义100个任务给线程池处理对比处理性能
long begin = System.currentTimeMillis();
for(int i=0;i<100;i++){
threadPoolExecutor.submit(new Task());
}
threadPoolExecutor.shutdown();
long end = System.currentTimeMillis();
System.out.println("处理时间:"+(end-begin)/1000+"秒");
}
}
class Task implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"处理完任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1.3 Pergunta 1 da entrevista: Parâmetros do conjunto de encadeamentos personalizados
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, unidade TimeUnit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, manipulador RejectedExecutionHandler) {corePoolSize: número de threads principais
int maximumPoolSize: o número máximo de threads
long keepAliveTime: tempo de sobrevivência do encadeamento temporário
Unidade TimeUnit: unidade de sobrevivência de thread temporária
BlockingQueue<Runnable> workQueue: fila de tarefas
ThreadFactory threadFactory: classe de fábrica personalizada geral
Manipulador RejectedExecutionHandler: estratégia de rejeição, existem 4 definições, geralmente podemos personalizar a estratégia de rejeição
AbortPolicy: RejectedExecutionException é lançada por padrão para rejeitar a tarefa DiscardPolicy: Abandona a tarefa DiscardOldestPolicy: Lança a tarefa mais antiga pela estrutura da fila CallerRunsPolicy: A thread que chama o método execute executa a tarefa
1.4 Esquema de Execução de Análise de Código-Fonte do Pool de Perguntas da Entrevista (TODO)
1.5 A diferença entre os métodos submit() e execute nas perguntas da entrevista (TODO)
A diferença entre enviar e executar: Existe um conceito de prioridade de envio e prioridade de execução no encadeamento, e a prioridade de envio é maior que a prioridade de execução. 1. O método execute está no método submit. 2. O método submit retornará uma função genérica Future, mas execute não retornará
1.6 A diferença entre offer() e add() em perguntas de entrevista
Ambos add e offer são métodos para adicionar tarefas à fila de tarefas.A diferença é que o método add não lança uma exceção, enquanto o offer lançará uma exceção de interrupção.Esta é a única diferença entre eles.
1.7 newScheduledThreadPool(TODO)
1.7.1 combate newScheduledThreadPool (TODO)
1.7.2 Análise do código-fonte (TODO)
1.8 Política de Negação Personalizada
Há duas maneiras de personalizar a política de rejeição:
//Estratégia de rejeição personalizada RejectedExecutionHandler rejeitedExecutionHandler = new RejectedExecutionHandler() { @Override public void failedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println("Estratégia de rejeição personalizada, pode ser armazenada no banco de dados"); } } ;O segundo método: implemente a interface RejectedExecutionHandler e reescreva o método rejeitadoExecution.
import java.util.concurrent.*;
/**
* 第31个任务会拒绝任务
*/
public class ThreadDemo {
public static void main(String[] args) {
//自定义拒绝策略
RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("自定义拒绝策略,可以存入数据库");
}
};
//定义ThreadPoolExecutor
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 30, 0, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),rejectedExecutionHandler);
//定义100个任务给线程池处理对比处理性能
long begin = System.currentTimeMillis();
for(int i=0;i<100;i++){
threadPoolExecutor.submit(new Task());
}
threadPoolExecutor.shutdown();
long end = System.currentTimeMillis();
System.out.println("处理时间:"+(end-begin)/1000+"秒");
}
}
class Task implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"处理完任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}