Thread
类;Runnable
接口;Callable
接口;Executors
工具类创建线程池。Thread
类Thread
类的子类,重写run
方法,将相关逻辑实现,run()
方法就是线程要执行的业务逻辑方法;star()
方法来启动线程。public class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法正在执行...");
}
}
public class TheadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println(Thread.currentThread().getName() + " main()方法执行结束");
}
}
main main()方法执行结束
Thread-0 run()方法正在执行...
Runnable
接口Runnable
接口实现类MyRunnable
,并重写run()
方法;MyRunnable
实例myRunnable
,以myRunnable
作为target
创建Thread
对象,该Thread
对象才是真正的线程对象;start()
方法。public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
}
}
public class RunnableTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
System.out.println(Thread.currentThread().getName() + " main()方法执行完成");
}
}
main main()方法执行完成
Thread-0 run()方法执行中...
Callable
接口Callable
接口的类myCallable
myCallable
为参数创建FutureTask
对象FutureTask
作为参数创建Thread
对象start()
方法public class MyCallable implements Callable<Integer> {
@Override
public Integer call() {
System.out.println(Thread.currentThread().getName() + " call()方法执行中...");
return 1;
}
}
public class CallableTest {
public static void main(String[] args) {
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
try {
Thread.sleep(1000);
System.out.println("返回结果 " + futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " main()方法执行完成");
}
}
Thread-0 call()方法执行中...
返回结果 1
main main()方法执行完成
Executors
工具类创建线程池答:Executors
提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService
接口。主要有newFixedThreadPool
,newCachedThreadPool
,newSingleThreadExecutor
,newScheduledThreadPool
。
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
}
}
public class SingleThreadExecutorTest {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
MyRunnable runnableTest = new MyRunnable();
for (int i = 0; i < 5; i++) {
executorService.execute(runnableTest);
}
System.out.println("线程任务开始执行");
executorService.shutdown();
}
}
线程任务开始执行
pool-1-thread-1 is running...
pool-1-thread-1 is running...
pool-1-thread-1 is running...
pool-1-thread-1 is running...
pool-1-thread-1 is running...
runnable
和 callable
有什么区别?Thread.start()
启动线程;Runnable
接口 run()
方法无返回值;Callable
接口 call
方法有返回值,是个泛型,和Future、FutureTask
配合可以用来获取异步执行的结果;Runnable
接口 run()
方法只能抛出运行时异常,且无法捕获处理;Callable
接口 call
方法允许抛出异常,可以获取异常信息;
Callalbe
接口支持返回执行结果,需要调用FutureTask.get()
得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
run()
和 start()
有什么区别?Thread
对象所对应的方法run()
来完成其操作的,run()
方法称为线程体。通过调用Thread
类的start()
方法来启动一个线程。start()
方法用于启动线程,run()
方法用于执行线程的运行时代码。run()
可以重复调用,而start()
只能调用一次。start()
方法来启动一个线程,真正实现了多线程运行。调用start()
方法无需等待run()
方法体代码执行完毕,可以直接继续执行其他的代码; 此时线程是处于就绪状态,并没有运行。 然后通过此Thread
类调用方法run()
来完成其运行状态, run()
方法运行结束, 此线程终止。然后**CPU
**再调度其它线程。run()
方法是在本线程里的,只是线程里的一个函数,而不是多线程的。 如果直接调用run()
,其实就相当于是调用了一个普通函数而已,直接待用run()
方法必须等待run()
方法执行完毕才能执行下面的代码,所以执行路径还是只有一条,根本就没有线程的特征,所以在多线程执行时要使用start()
方法而不是run()
方法。start()
方法时会执行run()
方法,为什么我们不能直接调用run()
方法?new
一个Thread
,线程进入了新建状态。调用 start()
方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。start()
会执行线程的相应准备工作,然后自动执行 run()
方法的内容,这是真正的多线程工作。run()
方法,会把run
方法当成一个main
线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。总结: 调用
start
方法方可启动线程并使线程进入就绪状态,而run
方法只是thread
的一个普通方法调用,还是在主线程里执行。
Callable
和 Future
?Callable
接口类似于Runnable
,从名字就可以看出来了,但是 Runnable
不会返回结果,并且无法抛出返回结果的异常,而 Callable
功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future
拿到,也就是说,Future
可以拿到异步执行任务的返回值。Future
接口表示异步任务,是一个可能还没有完成的异步任务的结果。所以说 Callable
用于产生结果,Future
用于获取结果。FutureTask
?答:FutureTask
表示一个异步运算的任务。FutureTask
里面可以传入一个 Callable
的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作。只有当运算完成的时候结果才能取回,如果运算尚未完成 get 方法将会阻塞。一个 FutureTask
对象可以对调用了 Callable
和 Runnable
的对象进行包装,由于 FutureTask
也是Runnable
接口的实现类,所以 FutureTask
也可以放入线程池中。
Future
和FutureTask
的区别 /**
Future的使用方法
**/
ExecutorService service = Executors.newSingleThreadExecutor();
Future<String> future = service.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "say helloWorld!!!";
}
});
System.out.println(future.get());// 通过get返回结果
/**
FutureTask
**/
ExecutorService service = Executors.newSingleThreadExecutor();
FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
return "futureTask say HelloWorld!!!";
}
});
service.execute(futureTask);
System.out.println(futureTask.get());
newSingleThreadExecutor
: 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO
, LIFO
,优先级)执行。newFixedThreadPool
: 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。newCachedThreadPool
: 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。newScheduledThreadPool
: 创建一个周期性线程池,支持定时及周期性任务执行。corePoolSize
: 表示当前线程池的核心线程数大小,即最小线程数(初始化线程数),线程池会维护当前数据的线程在线程池中,即使这些线程一直处于闲置状态,也不会被销毁;maximumPoolSize
: 表示线程池中允许的最大线程数;后文中会详细讲解keepAliveTime
: 表示空闲线程的存活时间,当线程池中的线程数量大于核心线程数且线程处于空闲状态,那么在指定时间后,这个空闲线程将会被销毁,从而逐渐恢复到稳定的核心线程数数量;unit
: 当前unit
表示的是keepAliveTime
存活时间的计量单位,通常使用TimeUnit.SECONDS
秒级;workQueue
: 任务工作队列;后文会结合maximumPoolSize
一块来讲threadFactory
: 线程工厂,用于创建新线程以及为线程起名字等handler
: 拒绝策略,即当任务过多无法及时处理时所需采取的策略;
AbortPolicy
:丢弃任务并抛出 RejectedExecutionException
异常。(默认这种)DiscardPolicy
:也是丢弃任务,但是不抛出异常DiscardOldestPolicy
:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)CallerRunsPolicy
:调用线程处理该任务,即创建了线程池的线程来执行被拒绝的任务IO
密集型: 一般线程数需要设置2倍CPU
数以上,以此来尽量利用CPU
资源。CPU
密集型: 一般线程数量只需要设置CPU
数加1即可,更多的线程数也只能增加上下文切换,不能增加CPU
利用率。ArrayBlockingQueue
: 是一个基于数组结构的有界阻塞队列,此队列按 FIFO
(先进先出)原则对元素进行排序。LinkedBlockingQueue
: 一个基于链表结构的阻塞队列,此队列按FIFO
(先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue
。静态工厂方法Executors.newFixedThreadPool()
使用了这个队列SynchronousQueue
: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue
,静态工厂方法Executors.newCachedThreadPool
使用了这个队列。PriorityBlockingQueue
: 一个具有优先级的无限阻塞队列。