Java创建线程的三种方式详解
前言
JAVA创建线程一共有三种方式,Thread类,Runable接口,Callable接口。
继承Thread类
创建并启动多线程步骤
- 继承Thread类,并重写run()方法,该run()方法的方法体就代表了线程需要完成的任务。可以把run()方法称为线程执行体。
- 创建Thread子类实例。
- 调用线程对象的start()方法来启动该线程。
代码示例
/**
* @ClassName: FirstThread
* @Description: 集成Thread方式实现多线程
* @author: 陈琼
* @connect:907172474@qq.com
* @date: 2021年2月11日 下午2:20:17
*/
public class FirstThread extends Thread {
private int i;
public void run() {
for (; i < 20; i++) {
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
// System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 5) {
new FirstThread().start();
new FirstThread().start();
}
}
}
}
输出结果:
Thread-0 0
Thread-1 0
Thread-0 1
Thread-1 1
Thread-0 2
Thread-0 3
Thread-0 4
Thread-0 5
Thread-0 6
Thread-0 7
Thread-0 8
Thread-0 9
Thread-0 10
Thread-0 11
Thread-0 12
Thread-0 13
Thread-0 14
Thread-0 15
Thread-0 16
Thread-0 17
Thread-0 18
Thread-0 19
Thread-1 2
Thread-1 3
Thread-1 4
Thread-1 5
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9
Thread-1 10
Thread-1 11
Thread-1 12
Thread-1 13
Thread-1 14
Thread-1 15
Thread-1 16
Thread-1 17
Thread-1 18
Thread-1 19
实现Runnable接口
- 实现Runable接口,并实现run方法;
- 创建Runable实例,并将此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
- 通过Thread对象创建线程后,调用start()方法启动线程。
/**
* @ClassName: SecondThread
* @Description: 实现Runable接口方式实现多线程
* @author: 陈琼
* @connect:907172474@qq.com
* @date: 2021年2月11日 下午2:31:19
*/
public class SecondThread implements Runnable {
private int i;
@Override
public void run() {
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
// System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 20) {
SecondThread st = new SecondThread();
new Thread(st, "新线程1").start();
new Thread(st, "新线程2").start();
}
}
}
}
输出结果:
新线程1 0
新线程2 0
新线程1 1
新线程2 2
新线程2 4
新线程2 5
新线程1 3
新线程2 6
新线程1 7
新线程2 8
新线程2 10
新线程2 11
新线程2 12
新线程2 13
新线程1 9
新线程2 14
新线程1 15
新线程2 16
新线程2 18
新线程1 17
新线程2 19
通过Callable和Future创建线程
- 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
- 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
- 使用FutureTask对象作为Thread对象的target创建并启动新线程。
- 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
Lambda写法
/**
* @ClassName: ThirdThread
* @Description: Callable和FeatureTask实现线程Lambda写法
* @author: 陈琼
* @connect:907172474@qq.com
* @date: 2021年2月11日 下午3:28:13
*/
public class ThirdThread {
public static void main(String[] args) {
FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>) () -> {
int i = 0;
for (; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + " 循环变量i的值:" + i);
}
return i;
});
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + " 的循环变量i的值:" + i);
if (i == 5) {
new Thread(task, "线程1").start();
}
}
try {
System.out.println("线程1:" + task.get());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
输出结果
main 的循环变量i的值:0
main 的循环变量i的值:1
main 的循环变量i的值:2
main 的循环变量i的值:3
main 的循环变量i的值:4
main 的循环变量i的值:5
main 的循环变量i的值:6
main 的循环变量i的值:7
main 的循环变量i的值:8
main 的循环变量i的值:9
main 的循环变量i的值:10
main 的循环变量i的值:11
main 的循环变量i的值:12
main 的循环变量i的值:13
main 的循环变量i的值:14
main 的循环变量i的值:15
main 的循环变量i的值:16
main 的循环变量i的值:17
main 的循环变量i的值:18
main 的循环变量i的值:19
线程1 循环变量i的值:0
线程1 循环变量i的值:1
线程1 循环变量i的值:2
线程1 循环变量i的值:3
线程1 循环变量i的值:4
线程1 循环变量i的值:5
线程1 循环变量i的值:6
线程1 循环变量i的值:7
线程1 循环变量i的值:8
线程1 循环变量i的值:9
线程1 循环变量i的值:10
线程1 循环变量i的值:11
线程1 循环变量i的值:12
线程1 循环变量i的值:13
线程1 循环变量i的值:14
线程1 循环变量i的值:15
线程1 循环变量i的值:16
线程1 循环变量i的值:17
线程1 循环变量i的值:18
线程1 循环变量i的值:19
线程1:20
实现接口写法
/**
* @ClassName: ThirdThread2
* @Description: Callable和FeatureTask实现线程实现接口写法写法
* @author: 陈琼
* @connect:907172474@qq.com
* @date: 2021年2月11日 下午3:50:52
*/
public class ThirdThreadIntf implements Callable<Integer> {
int i = 0;
@Override
public Integer call() throws Exception {
for (; i < 20; i++) {
try {
Thread.sleep(new Random().nextInt(100));
System.out.println(Thread.currentThread().getName() + " 循环变量i的值:" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "执行完成");
return i;
}
public static void main(String[] args) {
Callable a = new ThirdThreadIntf();
Callable b = new ThirdThreadIntf();
FutureTask<Integer> task1 = new FutureTask<>(a);
FutureTask<Integer> task2 = new FutureTask<>(a);
FutureTask<Integer> task3 = new FutureTask<>(b);
new Thread(task1, "线程一").start();
new Thread(task2, "线程二").start();
new Thread(task3, "线程三").start();
}
}
执行结果:
线程一 循环变量i的值:0
线程三 循环变量i的值:0
线程二 循环变量i的值:1
线程三 循环变量i的值:1
线程三 循环变量i的值:2
线程一 循环变量i的值:2
线程一 循环变量i的值:3
线程二 循环变量i的值:4
线程三 循环变量i的值:3
线程三 循环变量i的值:4
线程一 循环变量i的值:5
线程二 循环变量i的值:6
线程三 循环变量i的值:5
线程二 循环变量i的值:7
线程二 循环变量i的值:8
线程三 循环变量i的值:6
线程一 循环变量i的值:9
线程二 循环变量i的值:10
线程一 循环变量i的值:11
线程三 循环变量i的值:7
线程三 循环变量i的值:8
线程二 循环变量i的值:12
线程一 循环变量i的值:13
线程二 循环变量i的值:14
线程三 循环变量i的值:9
线程二 循环变量i的值:15
线程一 循环变量i的值:16
线程三 循环变量i的值:10
线程二 循环变量i的值:17
线程一 循环变量i的值:18
线程三 循环变量i的值:11
线程一 循环变量i的值:19
线程一执行完成
线程二 循环变量i的值:20
线程二执行完成
线程三 循环变量i的值:12
线程三 循环变量i的值:13
线程三 循环变量i的值:14
线程三 循环变量i的值:15
线程三 循环变量i的值:16
线程三 循环变量i的值:17
线程三 循环变量i的值:18
线程三 循环变量i的值:19
线程三执行完成
说明:
- 以上线程一与线程三共享i变量,两个线程连续基数;
- 这种使用FeatureTack和Callable方式实现线程,多线程之间共享数据时Feature需要使用同一个Callable对象,如上task1和task2;如果使用尝试Thread使用同一个Feature实现多线程数据共享会发现只执行一个,这是由于FutureTask内Callable对象的状态已经不是NEW(请自行源码)。使用Feature和Callable实现多线程时,线程Thread的target是Callable对象而不是Feature,只有使用相同的target才可实现线程间数据共享。
错误代码如下:
/**
* @ClassName: ThirdThread2
* @Description: Callable和FeatureTask实现线程实现接口写法写法
* @author: 陈琼
* @connect:907172474@qq.com
* @date: 2021年2月11日 下午3:50:52
*/
public class ThirdThreadIntf implements Callable<Integer> {
int i = 0;
@Override
public Integer call() throws Exception {
for (; i < 20; i++) {
try {
Thread.sleep(new Random().nextInt(100));
System.out.println(Thread.currentThread().getName() + " 循环变量i的值:" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "执行完成");
return i;
}
public static void main(String[] args) {
Callable a = new ThirdThreadIntf();
//Callable b = new ThirdThreadIntf();
FutureTask<Integer> task1 = new FutureTask<>(a);
//FutureTask<Integer> task2 = new FutureTask<>(a);
//FutureTask<Integer> task3 = new FutureTask<>(b);
new Thread(task1, "线程一").start();
new Thread(task1, "线程二").start();
new Thread(task1, "线程三").start();
}
}
执行结果:
线程一 循环变量i的值:0
线程一 循环变量i的值:1
线程一 循环变量i的值:2
线程一 循环变量i的值:3
线程一 循环变量i的值:4
线程一 循环变量i的值:5
线程一 循环变量i的值:6
线程一 循环变量i的值:7
线程一 循环变量i的值:8
线程一 循环变量i的值:9
线程一 循环变量i的值:10
线程一 循环变量i的值:11
线程一 循环变量i的值:12
线程一 循环变量i的值:13
线程一 循环变量i的值:14
线程一 循环变量i的值:15
线程一 循环变量i的值:16
线程一 循环变量i的值:17
线程一 循环变量i的值:18
线程一 循环变量i的值:19
线程一执行完成
三种实现方式对比
使用Runnable、Callable接口的方式
- 优势:线程类只是实现了Runnable接口或Callable接口,还可以集成其他类。
- 优势:多个线程可以共享一个target对象,非常适合多线程来处理同一份资源的情况,实现线程间数据共享。
- 劣势:编程稍微复杂,如果要获取当前线程,则必须使用Thread.currentThread()。
使用集成Thread类方式
- 劣势:因为线程已经集成了Thread类,所以不能再集成其他类了。
- 优势:编写简单,如果要获取当前线程,无需使用Thread.currentThread(),直接使用this即可获得当前线程。
正文到此结束