为什么要使用 多线程 ?在 Java 开发中,多线程并发是比较常见的业务场景。比如一些费时任务的处理(如:上百万数据的处理)这个时候就需要使用多线程处理。但是多线程是一把双刃剑,用得好,代码数据处理能力也能得到大幅提升。用得不好,可能会带来灾难性的后果(如:服务器资源耗尽,造成服务宕机)。下面就从实战的角度出发,探讨一下如何正确使用多线程。
一、java开发中实现多线程方式有哪些
1、继承 Thread 类,重写run()方法
2、实现Runnable接口,重写run()方法
3、实现Callable接口,重写call()方法 并使用FutureTask获取call( )方法的返回结果
4、 通过 线程池 创建 线程
下面我们用代码示例来实现前三种写法:
package com.example.jacob.thread;
/**
* 利用Thread类实现多线程
* @author yangzheng
* @create 2022-05-28
*/public class ThreadCreateByThreadClass {
public static void main(String[] args) {
//创建第一个子线程
JacobThread firstThread = new JacobThread();
firstThread.start();
String firstThreadName = firstThread.getName();
System.out.println("firstThreadName =" + firstThreadName);
//创建第二个子线程
JacobThread secondThread = new JacobThread();
secondThread.start();
String secondThreadName = secondThread.getName();
System.out.println("secondThreadName =" + secondThreadName);
}
}
/**
* JacobThread 继承 Thread类
*/class JacobThread extends Thread {
@Override
public void run() {
super.run();
//获取线程名称
String threadName = Thread.currentThread().getName();
for (int i = 0; i < 3; i++) {
System.out.println(threadName + ",i=" + i);
}
}
}
运行结果如下:
firstThreadName =Thread-0
Thread-0,i=0
Thread-0,i=1
Thread-0,i=2
secondThreadName =Thread-1
Thread- 1,i=0
Thread-1,i=1
Thread-1,i=2
package com.example.jacob.thread;
/**
* 利用Runnable接口实现多线程
* 主要步骤
* 1、创建Runnable接口实现类并重写该接口的run( )方法
* 2、创建Runnable接口实现类对象
* 3、利用Thread有参构造函数public Thread(Runnable target)和Runnable接口实现类对象创建线程实例
* 4、调用线程实例的start( )方法启动线程
* @author yangzheng
* @create 2022-05-28
*/public class ThreadCreateByInterface {
public static void main(String[] args) {
// 创建第一个子线程
JacobRunnable firstMyRunnable=new JacobRunnable();
Thread firstThread = new Thread(firstMyRunnable);
firstThread.start();
String firstThreadName = firstThread.getName();
System.out.println("firstThreadName=" + firstThreadName);
// 创建第二个子线程
JacobRunnable secondMyRunnable=new JacobRunnable();
Thread secondThread = new Thread(secondMyRunnable);
secondThread.start();
String secondThreadName = secondThread.getName();
System.out.println("secondThreadName="+ secondThreadName);
}
}
/**
* MyRunnable实现Runnable接口
*/class JacobRunnable implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
for (int i = 0; i < 3; i++) {
System.out.println(threadName + "i=" + i);
}
}
}
运行结果如下
firstThreadName=Thread-0
secondThreadName=Thread-1
Thread-0i=0
Thread-0i=1
Thread-0i=2
Thread-1i=0
Thread-1i=1
Thread-1i=2
package com.example.jacob.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.Execution Exception ;
import java.util.concurrent.FutureTask;
/**
* 利用Runnable接口实现多线程
* 主要步骤
1、创建Callable接口实现类并重写该接口的call( )方法\
2、创建Callable接口实现类对象
3、使用Runnable子类FutureTask的有参构造函数public FutureTask(Callable< V > callable)和Callable接口实现类对象创建FutureTask实例
4、利用Thread有参构造函数public Thread(Runnable target)和FutureTask实例创建线程实例
5、调用线程实例的start( )方法启动线程
6、利用FutureTask的get( )方法获取子线程执行结果
* @author yangzheng
* @create 2022-05-28
*/public class ThreadCreateByCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建第一个子线程
JacobCallable firstMyCallable = new JacobCallable();
FutureTask firstFutureTask = new FutureTask<>(firstMyCallable);
Thread firstThread = new Thread(firstFutureTask);
firstThread.start();
String firstThreadName = firstThread.getName();
System.out.println("firstThreadName="+ firstThreadName);
// 获取第一个子线程返回的结果
Object firstThreadResult = firstFutureTask.get();
System.out.println("firstThreadResult="+ firstThreadResult);
// 创建第二个子线程
JacobCallable secondMyCallable = new JacobCallable();
FutureTask secondFutureTask = new FutureTask<>(secondMyCallable);
Thread secondThread = new Thread(secondFutureTask);
secondThread.start();
String secondThreadName = secondThread.getName();
System.out.println("secondThreadName=" + secondThreadName);
// 获取第二个子线程返回的结果
Object secondThreadResult = secondFutureTask.get();
System.out.println("secondThreadResult=”" + secondThreadResult);
}
}
/**
* MyCallable实现Callable接口
*/class JacobCallable implements Callable {
@Override
public Object call() throws Exception {
String threadName = Thread.currentThread().getName();
int i = 0;
while (i < 3) {
System.out.println(threadName + ", i ="+ i);
i++;
}
return i;
}
}
firstThreadName=Thread-0
Thread-0, i =0
Thread-0, i =1
Thread-0, i =2
firstThreadResult=3
secondThreadName=Thread-1
Thread-1, i =0
Thread-1, i =1
Thread-1, i =2
secondThreadResult=”3
二、线程池创建多线程
线程池: 从本质上来讲, 就是⼀个容器,⾥⾯存储了若⼲个线程。
解决问题 :最主要是解决线程复⽤的问题。当执行线程时,不需要频繁的对线程进行新建和销毁,减少了 CPU 的负担。当需要一个线程时,不需要实例化,去线程池中查看是否有闲置的线程可以使用。如果有,则直接使用;如果没有,再去按照策略实例化新的线程。并且当线程使用完毕时,并不是马上销毁,而是根据配置策略判断是否需要放到线程池中,以便下次使用。
线程池中的所有线程可以分成两个部分:“核心线程”和”临时线程”
核心线程:核⼼线程一直存在于线程池中。当线程池销毁时,核心线程才会被销毁。
临时线程:临时新增线程,在处理完⾃⼰需要处理的任务后, 如果没有其他的任务要处理,就会空闲。当空闲的时间到达了指定的时间之后,这个临时线程就会被销毁。
java创建线程池可以分为 手动创建 和 自动创建 两种模式,
1、自动创建主要使用 Executors工具类。
2、 手动创建主要用 ThreadPoolExecutor 类,体现在可以灵活设置线程池的各个参数,体现在代码中即ThreadPoolExecutor类构造器上各个实参的不同:
本篇文章对线程池不做过多概述,后续文章再做详细介绍。
总结:
因为没有类的单继承的局限性,开发当中优先选择实现Runnable接口的方式来启动多线程