线程
- 创建线程的几种方式
- 1、继承Thread类
- 2、实现Runnable接口
- 3、实现Callable接口
- 4、线程池
- 创建线程几种方式
- 线程池详细解析
创建线程的几种方式
在创建线程之前,我们要先熟悉一个类Thread;
public class Thread implements Runnable {
private static native void registerNatives();
static {
registerNatives();
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
重写的Runnable接口的run方法,为业务运行代码编写的地方,如果我们要实现业务书写,也需要重写run方法,再通过点start方法启动线程,等待CPU调用!
1、继承Thread类
class ExtendsThread extends Thread{
@Override
public void run() {
System.out.println("继承Thread方式创建线程,重写run方法");
}
//启动线程方法见下方
}
2、实现Runnable接口
class RunnableImpl implements Runnable{
@Override
public void run() {
System.out.println("实现Runnable接口方式创建线程,重写run方法");
}
//启动线程方法见下方
}
3、实现Callable接口
class CallableImp implements Callable<Object>{
@Override
public Object call() throws Exception {
System.out.println("实现Callable接口方式创建线程,重写call方法,有返回值");
return "输出Callable的返回值";
}
//可以通过FutureTask类的其中一个构造方法使用Callable,另外一个构造方法可以使用Runnable
public static void main(String[] args) throws Exception {
CallableImp callableImp = new CallableImp();
FutureTask<Object> objectFutureTask = new FutureTask<>(callableImp);
Thread thread1 = new Thread(objectFutureTask);
thread1.start();
//打印返回值
System.out.println(objectFutureTask.get());
}
}
4、线程池
// 第四种,线程池
// 【强制--来自阿里巴巴Java开发手册】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,
// 这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险;
// ExecutorService executorService = Executors.newSingleThreadExecutor();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,10,5000, TimeUnit.MICROSECONDS,
new ArrayBlockingQueue<>(5),new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i <10 ; i++) {
//有返回值方式执行业务业务方法,参考第三种线程创建方式Callable;来自接口ExecutorService的submit(Runnable task)执行
Future<Object> submit = threadPoolExecutor.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
System.out.println("submit运行线程"+Thread.currentThread().getName());
return "线程池的submit(Callable)";
}
});
System.out.println("------------------"+submit.get());
//线程池无返回值方式执行业务方法,调用父类线程池Executor中的execute(Runnable command)实现;(new Runnable为匿名内部类!)
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println("execute运行线程"+Thread.currentThread().getName());
}
});
}
这里重点讲解线程池之前,简单过一下main中,使用线程的几种方式!
创建线程几种方式
public static void main(String[] args){
// 第一种创建方式:使用继承Thread的对象
ExtendsThread extendsThread = new ExtendsThread();
extendsThread.start();
// 第二种创建方式,使用runnable的实现类
RunnableImpl runnable = new RunnableImpl();
// 创建线程(使用有参构造函数)
Thread thread = new Thread(runnable);
// 启动线程,但是并不一定启动后立马执行哦
thread.start();
// 第三种,使用无参构造方法,重写Thread的run方法!:也可以直接使用用匿名内部类方式,具体其他创建方式还可查看Thread的其他构造函数(也可以使用匿名内部类)
new Thread(){
@Override
public void run() {
//我们的业务方法
}
}.start();
//匿名内部类(来自new Runnable接口!)
new Thread(new Runnable() {
@Override
public void run() {
//我们的run里面写的业务代码!
}
}).start();
//lamda表达式,等价于上面那种写法!!
new Thread(()->{
//我们的run里面写的业务代码!具体lamda表达式,后面找个专题细讲一下!
}).start();
线程池详细解析
首先我们来看看线程池的构造函数:
// Public constructors and methods
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
我们通过这个图,来理解里面的几个参数:
第一个参数:int corePoolSize 核心线程,上图绿色方框中的内容!
第二个参数:int maximumPoolSize 最大线程,核心线程和非核心线程的总数!
第三个参数:long keepAliveTime 存活时间,非核心线程的存活时间!
第四个参数:TimeUnit unit 存活时间单位,来自JUC中的TimeUnit枚举类!
第五个参数:BlockingQueue workQueue 阻塞队列 可以new 它的实现类,作为这个参数,配置上对应的长度即可 比如:new ArrayBlockingQueue(5) 长度五的阻塞队列!
第六个参数:RejectedExecutionHandler handler 异常处理方式,为红色部分所示!具体使用方式如下图所示,使用例子:new ThreadPoolExecutor.DiscardOldestPolicy()“丢弃老任务(队头)策略”
package com.woniuxy.concurrent.executor;
import java.util.concurrent.*;
public class ExecutorPoolTest1 {
volatile static int j = 0;
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MICROSECONDS,
new ArrayBlockingQueue<>(5), new ThreadPoolExecutor.AbortPolicy());
for (int i = 1; i <= 16; i++) {
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
System.out.println("我是"+Thread.currentThread().getName()+"线程,正在执行" + (++ExecutorPoolTest1.j) +
"的任务!");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},i);
}
}
}
打印结果:
我是pool-1-thread-1线程,正在执行1的任务!
我是pool-1-thread-4线程,正在执行4的任务!
我是pool-1-thread-2线程,正在执行2的任务!
我是pool-1-thread-3线程,正在执行3的任务!
我是pool-1-thread-5线程,正在执行5的任务!
我是pool-1-thread-6线程,正在执行6的任务!
我是pool-1-thread-7线程,正在执行7的任务!
我是pool-1-thread-8线程,正在执行8的任务!
我是pool-1-thread-9线程,正在执行9的任务!
我是pool-1-thread-10线程,正在执行10的任务!
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@5cad8086 rejected from java.util.concurrent.ThreadPoolExecutor@6e0be858[Running, pool size = 10, active threads = 10, queued tasks = 5, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:123)
at com.woniuxy.concurrent.executor.ExecutorPoolTest1.main(ExecutorPoolTest1.java:25)
我是pool-1-thread-7线程,正在执行11的任务!
我是pool-1-thread-4线程,正在执行13的任务!
我是pool-1-thread-10线程,正在执行12的任务!
我是pool-1-thread-5线程,正在执行14的任务!
我是pool-1-thread-8线程,正在执行15的任务!
通过上面这个方法,我们可以看出,如果线程数量正好是15(for循环次数)个线程,则线程池中可以总线程数量为10个,加上5个阻塞队列,正好可以执行15个任务,总共创建10个线程!如果切换异常执行方式,也就是最后一个参数,会有什么区别呢??请自行尝试!