创建线程的方式以及Executors的四种线程池

Posted 陳英傑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了创建线程的方式以及Executors的四种线程池相关的知识,希望对你有一定的参考价值。

创建线程有几种方式?
面试常问的问题,一般回答3种方式,及格;如果你能具体的说4种,那你就满分了,第四种是通过Executors创建线程池。

先来说说创建线程的3种方式:

一、继承Thread类

public class ExtendsThread extends Thread 

    @Override
    public void run() 
        super.run();
        Log.e("ExtendsThread >>> ", "thread running");
    

public class MainActivity 

	public static void main(String[] args) 
		ExtendsThread thread = new ExtendsThread();
		thread.start();
	

输出结果:

E/ExtendsThread >>>: thread running

二、实现Runnable接口

public class RunnableImp implements Runnable 
    @Override
    public void run() 
        Log.e("RunnableImp >>> ", "runnable running");
    

public class MainActivity 

	public static void main(String[] args) 
		RunnableImp runnableImp = new RunnableImp();
        new Thread(runnableImp).start();
	

输出结果:

E/RunnableImp >>>: runnable running

三、实现Callable接口

public class CallableImp implements Callable<String> 

    @Override
    public String call() throws Exception 
        Thread.sleep(3000);
        Log.e("CallableImp >>> ", "Callable running");
        return "I`m CallableImp";
    

public class MainActivity 

	public static void main(String[] args) 
		// 3、实现Callable接口
        try 
            CallableImp callableImp = new CallableImp();
            FutureTask<String> futureTask = new FutureTask<>(callableImp);
            new Thread(futureTask).start();
            // 如果CallableImp中任务未执行完,get()方法会阻塞
            Log.e("CallableImp 返回结果 >>> ", futureTask.get());
         catch (Exception e) 
            e.printStackTrace();
        
	

输出结果:

E/CallableImp >>>: Callable running
E/CallableImp 返回结果 >>>: I`m CallableImp

Runnable和Callable区别:
Runnable:
1、run方法没有返回值;
2、可以作为Thread的目标对象;
3、run方法不能抛出异常;

Callable:
1、call方法有返回值,通过FutureTask.get()方法获取,如果调用get方法时call方法没有执行完,get方法会阻塞;
2、不能作为Thread的目标对象,RunnableFuture才可以,因为RunnableFuture是一个继承了Runnable和Future的接口,FutureTask是RunnableFuture的一个实现类,一般用FutureTask结合Callable使用;
3、call方法可以抛出异常;

四、Executors创建线程池

1、CachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

private void cachedThreadPool() 
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) 
            final int index = i;
            cachedThreadPool.execute(new Runnable() 
                @Override
                public void run() 
                    System.out.println("CachedThreadPool >> running " + index + ", Thread = " + Thread.currentThread().toString());
                    try 
                        Thread.sleep(1000);
                     catch (Exception e) 
                        e.printStackTrace();
                    
                
            );
        
    

打印结果:

CachedThreadPool >> running 0, Thread = Thread[pool-1-thread-1,5,main]
CachedThreadPool >> running 1, Thread = Thread[pool-1-thread-2,5,main]
CachedThreadPool >> running 2, Thread = Thread[pool-1-thread-3,5,main]
CachedThreadPool >> running 3, Thread = Thread[pool-1-thread-4,5,main]
CachedThreadPool >> running 4, Thread = Thread[pool-1-thread-5,5,main]
CachedThreadPool >> running 5, Thread = Thread[pool-1-thread-6,5,main]
CachedThreadPool >> running 6, Thread = Thread[pool-1-thread-7,5,main]
CachedThreadPool >> running 7, Thread = Thread[pool-1-thread-8,5,main]
CachedThreadPool >> running 8, Thread = Thread[pool-1-thread-9,5,main]
CachedThreadPool >> running 9, Thread = Thread[pool-1-thread-10,5,main]

每次执行的Thread信息都不一样,说明每执行一次都创建了一个新的线程。

2、FixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
定长线程池的大小最好根据系统资源进行设置。

private void fixedThreadPool() 
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) 
            final int index = i;
            fixedThreadPool.execute(new Runnable() 
                @Override
                public void run() 
                    System.out.println("FixedThreadPool >> running " + index + ", Thread = " + Thread.currentThread().toString());
                    try 
                        Thread.sleep(1000);
                     catch (Exception e) 
                        e.printStackTrace();
                    
                
            );
        
    

打印结果:

FixedThreadPool >> running 0, Thread = Thread[pool-1-thread-1,5,main]
FixedThreadPool >> running 1, Thread = Thread[pool-1-thread-2,5,main]
FixedThreadPool >> running 2, Thread = Thread[pool-1-thread-3,5,main]
FixedThreadPool >> running 3, Thread = Thread[pool-1-thread-4,5,main]
FixedThreadPool >> running 4, Thread = Thread[pool-1-thread-5,5,main]
FixedThreadPool >> running 5, Thread = Thread[pool-1-thread-4,5,main]
FixedThreadPool >> running 6, Thread = Thread[pool-1-thread-3,5,main]
FixedThreadPool >> running 9, Thread = Thread[pool-1-thread-2,5,main]
FixedThreadPool >> running 8, Thread = Thread[pool-1-thread-1,5,main]
FixedThreadPool >> running 7, Thread = Thread[pool-1-thread-5,5,main]

从执行的Thread的信息发现不规律,但是根据线程ID发现只有5个线程,也就是说哪个线程空闲哪个线程去执行任务。

3、SingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

private void singleThread() 
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 20; i++) 
            final int index = i;
            singleThreadExecutor.execute(new Runnable() 
                @Override
                public void run() 
                    System.out.println("SingleThreadExecutor >> running " + index + ", Thread = " + Thread.currentThread().toString());
                    try 
                        Thread.sleep(1000);
                     catch (Exception e) 
                        e.printStackTrace();
                    
                
            );
        
    

打印结果:

SingleThreadExecutor >> running 0, Thread = Thread[pool-1-thread-1,5,main]
SingleThreadExecutor >> running 1, Thread = Thread[pool-1-thread-1,5,main]
SingleThreadExecutor >> running 2, Thread = Thread[pool-1-thread-1,5,main]
SingleThreadExecutor >> running 3, Thread = Thread[pool-1-thread-1,5,main]
SingleThreadExecutor >> running 4, Thread = Thread[pool-1-thread-1,5,main]
SingleThreadExecutor >> running 5, Thread = Thread[pool-1-thread-1,5,main]
SingleThreadExecutor >> running 6, Thread = Thread[pool-1-thread-1,5,main]
SingleThreadExecutor >> running 7, Thread = Thread[pool-1-thread-1,5,main]
SingleThreadExecutor >> running 8, Thread = Thread[pool-1-thread-1,5,main]
SingleThreadExecutor >> running 9, Thread = Thread[pool-1-thread-1,5,main]

从执行的Thread信息发现ID都是同一个,此线程池中只有一个线程,任务是按顺序执行的。

4、ScheduleThreadPool
创建一个定长线程池,支持定时及周期性任务执行。

它有4个可调用方法:

// 1、延迟执行一个Runnable接口的实现
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);

// 2、延迟执行一个Callable接口的实现
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);

// 3、延迟之后以固定频率执行一个Runnable接口的实现
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);

// 4、延迟之后开始执行一个Runnable接口的实现,之后的每次任务都是在前一个任务结束后延迟delay再执行
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);

/*
3和4的区别:
3是不考虑每个任务的耗时,4要考虑每个任务的耗时,画一个时间轴来理解:

比如 scheduleAtFixedRate(runnable, 1000, 1000, TimeUnit.MILLISECONDS)
和	scheduleWithFixedDelay(runnable, 1000, 1000, scheduleWithFixedDelay)
假如每个任务耗时1秒,两者执行的时间轴(每个_代表1秒,|表示执行任务):
3: _|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|...
4: _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|...

/

例子:

private void scheduledThreadPool() 
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
//        scheduledThreadPool.scheduleWithFixedDelay(new Runnable() 
        scheduledThreadPool.scheduleAtFixedRate(new Runnable() 
            @Override
            public void run() 
                System.out.println(new Date().toString() + "<< ScheduledThreadPool >> running " + index + ", Thread = " + Thread.currentThread().toString());
                index++;
                try 
                    Thread.sleep(1000);
                 catch (Exception e) 
                    e.printStackTrace();
                
            
        , 1000, 1000, TimeUnit.MILLISECONDS);
    

方法3打印结果:

Thu Nov 05 15:59:17 CST 2020<< ScheduledThreadPool >> start
Thu Nov 05 15:59:18 CST 2020<< ScheduledThreadPool >> running 0, Thread = Thread[pool-1-thread-1,5,main]
Thu Nov 05 15:59:19 CST 2020<< ScheduledThreadPool >> running 1, Thread = Thread[pool-1-thread-1,5,main]
Thu Nov 05 15:59:20 CST 2020<< ScheduledThreadPool >> running 2, Thread = Thread[pool-1-thread-2,5,main]
Thu Nov 05 15:59:21 CST 2020<< ScheduledThreadPool >> running 3, Thread = Thread[pool-1-thread-1,5,main]
Thu Nov 05 15:59:22 CST 2020<< ScheduledThreadPool >> running 4, Thread = Thread[pool-1-thread-3,5,main]
Thu Nov 05 15:59:23 CST 2020<< ScheduledThreadPool >> running 5, Thread = Thread[pool-1-thread-3,5,main]
Thu Nov 05 15:59:24 CST 2020<< ScheduledThreadPool >> running 6, Thread = Thread[pool-1-thread-3,5,main]
Thu Nov 05 15:59:25 CST 2020<< ScheduledThreadPool >> running 7, Thread = Thread[pool-1-thread-3,5,main]
Thu Nov 05 15:59:26 CST 2020<< ScheduledThreadPool >> running 8, Thread = Thread[pool-1-thread-3,5,main]
Thu Nov 05 15:59:27 CST 2020<< ScheduledThreadPool >> running 9, Thread = Thread[pool-1-thread-3,5,main]

从开始到之后的每次延迟任务间隔都是1秒;

方法4打印结果:

Thu Nov 05 16:00:55 CST 2020<< ScheduledThreadPool >> start
Thu Nov 05 16:00:56 CST 2020<< ScheduledThreadPool >> running 0, Thread = Thread[pool-1-thread-1,5,main]
Thu Nov 05 16:00:58 CST 2020<< ScheduledThreadPool >> running 1, Thread = Thread[pool-1-thread-1,5,main]
Thu Nov 05 16:01:00 CST 2020<< ScheduledThreadPool >> running 2, Thread = Thread[pool-1-thread-2,5,main]
Thu Nov 05 16:01:02 CST 2020<< ScheduledThreadPool >> running 3, Thread = Thread[pool-1-thread-1,5,main]
Thu Nov 05 16:01:通过Executors创建线程池的四种方式和注意小点

Java线程池的四种创建方式

Executors 提供的四种线程池

Executors提供的四种线程池

线程池基本使用详解

Java中常用的四种线程池