多线程的实现方式主要有两种:一种是继承Thread,一种是实现Runnable接口,以下6种实际上是对这两种实现方式的一种变形:
1)继承Thread
2)实现Runnable接口
3)匿名内部类
4)定时器
5)基于Callable实现多线程
6)线程池
1.继承Thread
1)编写一个类MyThread继承Thread,重写run方法
2)新建MyThread的实例
3)调用start方法
package MultiThread.type1;
public class MyThread extends Thread {
public MyThread(String name){
this.setName(name);//此处的setName方法是调用父类即Thread类的方法
}
@Override
public void run() {
while(true){
System.out.println("现在的线程是:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package MultiThread.type1;
//实现多线程的第一种方式,继承Thread
public class CreateThreadDemo1 {
public static void main(String[] args) {
new MyThread("hello").start();//此处使用MyThread的有参构造方法设置线程名称
while(true){
System.out.println("现在运行的线程是:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
现在运行的线程是:main
现在的线程是:hello
现在运行的线程是:main
现在的线程是:hello
2.实现Runnable接口
1)创建一个类CreateThreadDemo2,实现Runnable接口,重写run方法
2)新建CreateThreadDemo2对象task
3)新建Thread对象,并指向对象task
4)调用start方法
package MultiThread.type2;
//实现多线程的第二种方式,实现Runnable接口
//相较于继承Trhead这种实现方式,这种方式可以降低耦合度
public class CreateThreadDemo2 implements Runnable{
@Override
public void run() {
while(true){
System.out.println("现在运行的线程是:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package MultiThread.type2;
public class MyThread2 {
public static void main(String[] args) {
//实例化线程任务类
CreateThreadDemo2 task = new CreateThreadDemo2();
//创建线程对象,将线程任务类作为构造方法的参数出入
Thread t = new Thread(task);
t.setName("hello");
t.start();
while(true){
System.out.println("现在的线程是:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
现在的线程是:main
现在运行的线程是:hello
现在的线程是:main
现在运行的线程是:hello
观察Runnable源码,发现这个接口只有一个未实现的方法,那么就可使用Lambda来简化代码的书写
注:Lambda:JDK8的一个新特性。Lambda规定接口中只能有一个需要被实现的方法,而不是规定接口中只能有一个方法。语法形式为()->{},其中()为参数列表,->是Lambda运算符,{}是方法体,比如Runnable task=()->{//具体的功能},具体可参考Lambda表达式详解
package java.lang;
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
简化后的书写方式是:
package MultiThread.type2;
//使用Lambda简化书写,jdk1.8后有Lambda
public class Lambda {
public static void main(String[] args) {
Runnable task=()->{
while(true){
System.out.println("现在运行的线程是:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
new Thread(task).start();
while(true){
System.out.println("现在的线程是:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
现在的线程是:main
现在运行的线程是:Thread-0
现在的线程是:main
现在运行的线程是:Thread-0
3.匿名内部类
其实这不是一种新的实现多线程的方式,只是一种写法。它又细化为继承Thread和实现Runnable两种方式。如果既既基于接口,又基于子类,经过测试,其实会基于子类运行
package MultiThread.type3;
//第3种方式,通过匿名内部类方式实现(它不是一种新的实现方式,只是一种写法)
//又细化为继承Thread以及实现Runnable
//如果既基于接口,又基于子类,那么其实会基于子类运行
public class AnonymosClass {
public static void main(String[] args) {
//1.继承Thread
new Thread(){
@Override
public void run() {
while(true) {
System.out.println("现在运行的线程是:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
//2.实现Runnable接口
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println("现在运行的线程是:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
//3.既基于接口,又基于子类,那么其实会基于子类运行
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println("现在运行的线程是基于接口的线程:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}){
@Override
public void run() {
while(true) {
System.out.println("现在运行的线程是基于子类的线程:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
while(true){
System.out.println("现在的线程是:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
现在运行的线程是:Thread-0
现在运行的线程是:Thread-1
现在的线程是:main
现在运行的线程是基于子类的线程:Thread-2
现在的线程是:main
现在运行的线程是基于子类的线程:Thread-2
现在运行的线程是:Thread-1
现在运行的线程是:Thread-0
使用Lambda表达式改造匿名内部类方式中实现接口的形式,使代码更简洁:
package MultiThread.type3;
//使用Lambda改造匿名内部类中实现接口的形式,使代码更加简洁
public class Lambda {
public static void main(String[] args) {
//不适用Lambda
new Thread(new Runnable() {
@Override
public void run() {
//。。。
}
}).start();
//使用Lambda:
new Thread(()->{
//。。。
}).start();
}
}
4.定时器
实现定时器的方式有多种,如JDK提供的Timer,Spring的schedule,quartz。这里用JDK提供的Timer类来实现定时器。
可以指定时间让定时器执行一次,也可间隔时间重复执行。
package MultiThread.type4;
//第4种实现多线程的方式,定时器
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TimerThread {
private static final SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws ParseException {
//1.指定时间
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("定时任务开始执行。。。");
}
},sdf.parse("2021-03-12 20:45:00"));//到达指定时间即运行一次
//2.间隔时间重复执行
Timer timer1=new Timer();
timer1.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("定时任务2开始执行。。。");
}
},new Date(),1000);//每隔1秒运行一次
}
}
运行结果:
定时任务开始执行。。。
定时任务2开始执行。。。
定时任务2开始执行。。。
定时任务2开始执行。。。
5.基于Callable实现多线程
以上几种方式都有两个问题:1.无法抛出更多的异常;2.线程执行完毕之后不能拿到返回值。
Callable接口类似于Runnable接口,但比Runnable接口更强大,增加了异常和返回值
1)创建一个类实现Callable接口,实现call方法。
2)创建一个FutureTask,指向Callable对象,作为线程任务
3)创建线程,指定线程任务
4)启动线程
package MultiThread.type5;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//以上几种方式都有两个问题:1.无法抛出更多的异常;2.线程执行完毕之后不能拿到线程返回值。
//实现多线程的第5种方式,带返回值的线程实现方式,步骤:
//1.创建一个类实现Callable接口,实现call方法。这个接口类似于Runnable接口,但比Runnable接口更强大,增加了异常和返回值
//2.创建一个FutureTask,指向Callable对象,作为线程任务;
//3.创建线程,指定线程任务;
//4.启动线程。
public class CreateThreadDemo5 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Integer a=1;
Callable<Integer> call=()->{
System.out.println("异步线程任务开始");
return 1;
};
FutureTask<Integer> task = new FutureTask<>(call);
new Thread(task).start();
//这里可以写一些别的代码
Integer i = task.get();//拿到线程返回值
System.out.println("主线程拿到异步线程返回结果:"+i);
}
}
运行结果:
异步线程任务开始
主线程拿到异步线程返回结果:1
6.线程池
package MultiThread.type6;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//实现多线程的第6种方式,线程池的方式。
public class CreateThreadDemo6 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(10);
while(true){
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("当前线程是:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
运行结果:
当前线程是:pool-1-thread-1
当前线程是:pool-1-thread-5
当前线程是:pool-1-thread-4
当前线程是:pool-1-thread-3
当前线程是:pool-1-thread-2
当前线程是:pool-1-thread-8
当前线程是:pool-1-thread-7
当前线程是:pool-1-thread-6
当前线程是:pool-1-thread-10
当前线程是:pool-1-thread-9
参考:实现多线程的几种方式