Java创建多线程的四种方式
- 继承Thread类
- 实现Runnable接口
- 使用FutureTask和Callable
- 使用线程池
一、继承Thread类
- 创建一个Thread类的子类
- 在子类中重写run()方法,设置线程任务
- 创建子类对象
- 调用Thread类中的start()方法开启新的线程
代码实例
public class MyThread{
public static void main(String[] args) {
//1、创建一个Thread
Thread thread = new Thread("newThread") {
@Override
public void run() {
//2、重写run()方法,设置线程任务
for (int i = 0; i < 5; i++){
System.out.println("run_"+i+"_"+Thread.currentThread().getName());
}
}
};
//3、调用Thread类中的start()方法开启新的线程
thread.start();
for (int i = 0; i < 5; i++) {
System.out.println("main"+i+"_"+Thread.currentThread().getName());
}
}
}
运行结果
二、实现Runnable接口
public class MyThread2{
public static void main(String[] args) {
Runnable r = new Runnable(){
@Override
public void run() {
//2、重写run方法并设置线程任务
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
};
//java8使用Lambda简化
/*Runnable r = ()->{
//2、重写run方法并设置线程任务
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
};*/
//4、创建Thread类对象,构造方法中传递Runnable接口的实现类对象以及线程名称
Thread t = new Thread(r,"newThread");
//5、调用Thread类中的start方法,开启新的线程执行run方法
t.start();
for (int i = 0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
运行结果
三、使用FurtureTask和Callable
代码实例
public class MyThread3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1、创建FutureTask 并传入 Callable
FutureTask futureTask = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
//2、重写call方法并设置线程任务
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
Thread.sleep(5000);
return 0;
}
});
//3、创建Thread线程并启动
Thread newThread = new Thread(futureTask, "newThread");
newThread.start();
//4、获取返回值
System.out.println("back:"+futureTask.get());
//获取到返回值后才继续执行以下代码
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
运行结果
四、使用线程池
概念: 容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建的操作。
线程池使用步骤:
- 使用线程池的工厂类Executors里提供的静态方法newFixedThreadPool生产一个指定线程数的线程池。
- 创建一个类,实现Runnable接口,重写run方法,设置线程任务
- 调用ExecutorService中的submit方法,传递线程任务(实现类),开启线程,执行run方法
- 调用ExecutorService中的shutdown方法销毁线程池(不建议执行)
使用线程池的好处:
- 降低资源消耗。减少了创建和销毁线程的次数,每个线程都可以被重复利用,可执行多个任务
- 提高响应速度。当任务到达时无需等待线程创建就可立即执行
- 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线程的数量,防止消耗过多内存,(每个线程大约需要1M内存,线程开的越多,消耗的内存也就越多)
底层原理
七大参数:
1、int corePoolSize:
初始化线程数、常驻核心线程数
2、int maximumPooSize:
线程池中能容纳同时执行的最大线程数,此值必须大于等于1
3、long keepAliveTime:
多余的空闲线程存活时间,当线程池中的数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余空闲线程会销毁到只剩下corePoolSize个线程。
4、TimeUnit unit:
keepAliveTime的单位
5、BlockingQueue<Runnable>
workQueue:
任务队列,等待队列。
6、ThreadFactory threadFactory:
生成线程池中工作线程的线程工厂,用于创建线程,一般使用默认的
7、RejectedExecutionHandler handler:
拒绝策略,当workQueue满了之后,工作线程大于等于maximumPooSize时,如何拒绝Runnable的策略
- AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行
- CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量
- DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务
- DiscardPolicy:直接丢弃任务,不予任何处理也不抛出异常。如果任务允许丢失,这是最好的选择
以上策略均实现了RejectedExecutionHandle接口。
代码实例
public class MyThread4 {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
es.submit(new RunnableTemp());
}
}
}
class RunnableTemp implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
运行结果
学习笔记,欢迎纠正补充~