在java进程中,每一个线程都对应着一个Thread实例,线程的描述信息都会保存在Thread实例属性中, 供JVM进行线程管理和调度时使用功能.Thread 类是java多线程编程的基础, 创建线程有以下4中方式:
- 继承Thread类创建线程类
- 实现Runnable接口创建线程目标类
- 使用Callable 和FutureTask 创建线程
- 通过线程池创建线程
继承Thread类创建线程类
继承Thread类创建线程类是java多线程编程的最基础的创建方式,继承Thread类的同时需要重写run()方法,将需要并发执行的业务代码编写在run()方法中,示例代码如下:
package com.th.thread;
/**
* @ClassName: CreateDemo
* @Description:
* @Author: 唐欢
* @Date: 2022/8/11 15:24
* @Version 1.0
*/
public class CreateDemo {
public static final int MAX_TURE=5;
public static String getCurThreadName(){
return Thread.currentThread().getName();
}
//线程的编号
static int threadNo=1;
static class DemoThread extends Thread{
public DemoThread(){
super("DemoThrea-"+threadNo++);
}
@Override
public void run(){
for (int i=1;i<MAX_TURE;i++){
System.out.println(getName()+",轮次:"+i);
}
System.out.println(getName() + "运行结束,哈哈");
}
}
public static void main(String[] args) {
Thread thread =null;
//方法一:使用Thread子类创建和启动线程
for (int i=0;i<2;i++){
thread =new DemoThread();
thread.start();
}
System.out.println(ThreadUtil.getCurThreadName() + "最终运行结束");
}
}
运行结果如下:
从代码中可以看到,设计了一个静态内部类DemoThread,方便防伪外部类的成员属性和方法,静态内部类DemoThread 重写了Thread类的run()方法,并将需要并发执行的用户业务代码编写在重写的run()方法中.
在创建线程的时候继承Thread方法需要重写run()方法, 接下来我们就来看下Thread.run()方法的实现代码:
从代码中可看到,如果target(执行目标)不为空,就执行target属性的run方法.target 属性的定义如下:
target 属性是Thread 属性是Thread 类的一个实例属性,并且target属性的类型为Runable.
实现Runnable接口创建线程目标类
Runnable 是一个极为简单的接口,代码如下:
Runnable接口有且仅有一个抽象方法 run(),并被用注解@FunctionalInterface 标注为函数式接口. 在使用的时候,将用户业务逻辑编写在run()方法中. 当Runnable 实例传入Thread 实例的target属性后,Runnable 接口的run()的实现版本被异步调用.
我们先来看一个通过实现Runnable接口创建线程的示例:
package com.th.runnable;
import com.th.util.ThreadUtil;
/**
1. @ClassName: CreateDemo
2. @Description:
3. @Author: 唐欢
4. @Date: 2022/8/11 15:35
5. @Version 1.0
*/
public class CreateDemo {
public static final int MAX_TURN=5;
static int threadNo=1;
static class RunTarget implements Runnable{
@Override
public void run() {
for (int j=1 ;j<MAX_TURN;j++){
System.out.println(ThreadUtil.getCurThreadName()+",轮次:"+j);
}
System.out.println(ThreadUtil.getCurThreadName() +" 运行结束");
}
}
public static void main(String[] args) {
Thread thread =null;
for (int i=0;i<2;i++){
Runnable targe =new RunTarget();
//通过Thread 类创建线程对象,将Runnable 实例作为实际参数传入
thread = new Thread(targe,"RunnableThread"+threadNo++);
thread.start();
}
}
}
从示例代码中可看到,静态内部类RunTarget 执行目标类实现Runnable接口需要一部并发执行代码逻辑被编写在它的run()方法中.将Runnable 实例作为target 执行目标传入Thread实例. 具体实现步骤如下:
(1) 定义一个新类实现Runable接口;
(2) 实现runnable接口中的run()抽象方法,将线程代码逻辑存放在该run()实现版本中.
(3) 通过Thread类创建线程对象, 将Runnable 实例作为实际参数传递给Thread类的构造器,由Thread 构造器将该Runnable 实例赋值给自己的target 执行目标属性.
(4) 调用Thread实例的start()方法启动线程.
(5)线程启动之后,线程的run()方法将被JVM 执行,该run()方法将调用target 属性的run()方法,从而完成Runnable实现类中业务代码逻辑的并发执行.
使用Runnable 创建线程目标类除了直接实现Runnable 接口之外,还有两种比较优雅的实现方式,
通过匿名类创建Runnable线程目标类
在实现Runnable 接口创建线程目标类时,若target 实现类是一次性的,就可以使用匿名类实现,实现代码如下:
package com.th.runnable;
import com.th.util.ThreadUtil;
import java.time.chrono.ThaiBuddhistEra;
/**
* @ClassName: CreateDemo2
* @Description: 通过匿名类优雅地创建Runnable 线程目标类
* @Author: 唐欢
* @Date: 2022/8/11 16:21
* @Version 1.0
*/
public class CreateDemo2 {
public static final int MAX_TURN=5;
static int threadNo= 1;
public static void main(String[] args) {
Thread thread =null;
//使用Runnable的匿名类创建和启动线程
for (int i=0;i<2 ;i++){
thread =new Thread(new Runnable() {
@Override
public void run() {
for (int j=1;j<MAX_TURN;j++){
System.out.println(ThreadUtil.getCurThreadName()+",轮次:"+j);
}
System.out.println(ThreadUtil.getCurThreadName()+"运行结束.");
}
},"RunnableThread "+threadNo++);
thread.start();
}
System.out.println(ThreadUtil.getCurThreadName()+"最后运行结束了.");
}
}
使用Lambda 表达式创建Runnable线程目标类.
通过Lambda 表达式直接编写Runnable 接口的run()方法的实现代码,接口名称(Runnable) ,方法名称run() 统统都被省略,仅剩下了run()方法的形参列表和方法体,示例代码如下:
package com.th.runnable;
import com.th.util.ThreadUtil;
import java.awt.image.ShortLookupTable;
/**
* @ClassName: CreateLambdaDemo
* @Description:
* @Author: 唐欢
* @Date: 2022/8/11 16:33
* @Version 1.0
*/
public class CreateLambdaDemo {
public static final int MAX_TURN =5;
static int threadNo=1;
public static void main(String[] args) {
Thread thread =null;
//使用Lambda 表达式形式创建和启动线程
for (int i =0 ;i<2;i++){
thread =new Thread(()->{
for (int j=1;j<MAX_TURN;j++){
System.out.println(ThreadUtil.getCurThreadName() +",轮次:"+j);
}
System.out.println(ThreadUtil.getCurThreadName()+"运行结束.");
},"RunnableThread"+threadNo++);
thread.start();
}
System.out.println(ThreadUtil.getCurThreadName()+"最终运行结束了.");
}
}
使用Callable 和FutureTask 创建线程
虽然说是使用Thread和Runnable可以创建线程,但是这两中方法有一个共同的缺点:不能获取异步执行的结果.
在jdk1.5 后,提供了一种新的多线程创建方法,通过Callable接口和FutureTask类相结合创建线程.
通过Callable接口和Future接口相结合创建多线程能够获取异步执行结果,创建线程的步骤如下:
(1) 创建一个Callable 接口的实现类,并实现其Call()方法,编写好异步执行的具体逻辑,可以有返回值,
(2) 使用Callable实现类的实例构造一个FutureTask 实例.\
(3) 使用FutureTask实例作为Thread构造器的target入参,构造新的Thread 线程示例.
(4)调用Thread 实例的start()方法启动新线程,启动新线程的run()方法并发执行.其内部的执行过程为:启动Thread实例的run()方法并发执行后,会执行FutureTask实例的run()方法,最终会并发执行Callable实现类的call()方法.
(5)调用FutureTask 对象的get() 方法阻塞性地获得并发线程的执行结果.
示例的代码如下:
package com.th.callableandfuture;
import com.th.util.ThreadUtil;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @ClassName: CreateDemo
* @Description:
* @Author: 唐欢
* @Date: 2022/8/12 13:48
* @Version 1.0
*/
public class CreateDemo {
public static final int MAX_TURE=5;
public static final int COMPUTE_TIME=100000000;
/**
* ① 创建一个Callable 接口的实现类
*/
static class ReturnalbleTask implements Callable<Long>{
//② 编写好异步执行的具体逻辑,可以有返回值
@Override
public Long call() throws Exception {
Long startTime = System.currentTimeMillis();
System.out.println(ThreadUtil.getCurThreadName() +"线程运行开始,");
Thread.sleep(1000);
for (int i=0;i<COMPUTE_TIME;i++){
int j= i*10000;
}
long used =System.currentTimeMillis()-startTime;
System.out.println(ThreadUtil.getCurThreadName()+"线程运行结束");
return used;
}
}
public static void main(String[] args) throws InterruptedException {
ReturnalbleTask task = new ReturnalbleTask();
FutureTask<Long> futureTask = new FutureTask<Long>(task);
Thread thread = new Thread(futureTask,"returnableThread");
thread.start();
Thread.sleep(500);
System.out.println(ThreadUtil.getCurThreadName()+"让子弹飞一会儿,");
System.out.println(ThreadUtil.getCurThreadName()+"做一点自己的事情");
for (int i=0;i<COMPUTE_TIME/2;i++){
int j=i*10000;
}
System.out.println(ThreadUtil.getCurThreadName()+"获取并发任务的执行结果.");
try {
System.out.println(thread.getName()+"线程占用时间:"+futureTask.get());
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(ThreadUtil.getCurThreadName()+"运行结束.");
}
}
从示例代码中可知有两个线程,一个是执行main()方法的主线程main,另个是main线程通过Thread.start() 方法启动的业务线程ReturnalbleTask.main线程通过Thread.start()启动ReturnalbleTask线程之后,会继续做自己的事,ReturnalbleTask 线程开始并发执行, ReturnalbleTask 线程首先执行的是thread.run()方法,然后在其中会执行到target(futureTask任务) 的run()方法,接着在futureTask.run()方法中执行futureTask的callable成员的call()方法.FutureTask的callable成员的call()方法执行完成后,会将结果保存在FutureTask内部的outCome实例属性中.
main线程和returnableTask线程的执行流程大致如下:
通过线程池创建线程
前面三种方式创建的Thread实例在执行完成后就销毁了,这些线程实例都是不可复用的. 在高并发的应用常见下,是要求线程能够复用的,若想线程可以复用,就需要使用线程池技术.java中提供了了一个静态工厂来创建不同的线程池 --Executors工厂类.
使用Executors创建线程池,然后使用ExecutorService线程池执行或者提交target执行目标实例的示例代码如下:
package com.th.Executors;
import com.th.util.ThreadUtil;
import java.util.concurrent.*;
/**
* @ClassName: CreateDemo
* @Description:
* @Author: 唐欢
* @Date: 2022/8/12 14:18
* @Version 1.0
*/
public class CreateDemo {
public static final int MAX_TURE=5;
public static final int COMPUTE_TIMES=100000000;
//创建一个包含三个线程的线程池
private static ExecutorService pool = Executors.newFixedThreadPool(3);
static class DemoThread implements Runnable{
@Override
public void run() {
for (int j=1;j<MAX_TURE;j++){
System.out.println(ThreadUtil.getCurThreadName()+",轮次:"+j);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class ReturnableTask implements Callable<Long>{
// 返回并发执行的时间
@Override
public Long call() throws Exception {
Long startTime = System.currentTimeMillis();
System.out.println(ThreadUtil.getCurThreadName() +"线程运行开始,");
Thread.sleep(1000);
for (int i=0;i<MAX_TURE;i++){
int j= i*10000;
}
long used =System.currentTimeMillis()-startTime;
System.out.println(ThreadUtil.getCurThreadName()+"线程运行结束");
return used;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//执行线程实例,无返回
pool.execute(new DemoThread());
pool.execute(new Runnable() {
@Override
public void run() {
for (int j=1; j<MAX_TURE;j++){
System.out.println(ThreadUtil.getCurThreadName()+",轮次:"+j);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Future future = pool.submit(new ReturnableTask());
Long result = (Long) future.get();
System.out.println("异步任务的执行结果为:"+result);
Thread.sleep(Integer.MAX_VALUE);
}
}
补充ThreadUtil类的代码:
public class ThreadUtil {
public static String getCurThreadName(){
//获取线程名称
return Thread.currentThread().getName();
}
}