创建线程有几种方式?
面试常问的问题,一般回答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:04 CST 2020<< ScheduledThreadPool >> running 4, Thread = Thread[pool-1-thread-3,5,main]
Thu Nov 05 16:01:06 CST 2020<< ScheduledThreadPool >> running 5, Thread = Thread[pool-1-thread-3,5,main]
Thu Nov 05 16:01:08 CST 2020<< ScheduledThreadPool >> running 6, Thread = Thread[pool-1-thread-3,5,main]
Thu Nov 05 16:01:10 CST 2020<< ScheduledThreadPool >> running 7, Thread = Thread[pool-1-thread-3,5,main]
Thu Nov 05 16:01:12 CST 2020<< ScheduledThreadPool >> running 8, Thread = Thread[pool-1-thread-3,5,main]
Thu Nov 05 16:01:14 CST 2020<< ScheduledThreadPool >> running 9, Thread = Thread[pool-1-thread-3,5,main]
第一个任务是延迟1秒执行的,从第二个开始都是延迟2秒,这2秒是前一个任务执行耗时1秒,然后再延迟1秒执行下一个任务,所以从第二个任务开始都是延迟延迟2秒再执行。
根据上面打印规律可以发现执行任务的线程ID没有没有规律,它是个定长的线程池,有任务时,哪个线程空闲哪个线程来执行任务。

本文介绍了四种创建线程的方法:继承Thread类、实现Runnable接口、实现Callable接口和通过Executors创建线程池。并详细对比了Runnable和Callable的区别,以及线程池的不同类型及其应用场景。
644

被折叠的 条评论
为什么被折叠?



