具体步骤为:
1.继承Thread类,并重写run方法。run方法里面的逻辑就是线程要执行的逻辑。
2.创建子类的对象实例。
3.必须要使用对象实例的start方法启动该线程,不能直接使用run方法。
public class MyThread extends Thread {
@Override
public void run() {
for(int i=0; i<50; i++) {
String threadName = Thread.currentThread().getName();
System.out.println("cur thread is: " + threadName + ", i is: " + i);
}
}
public static void main(String[] args) {
MyThread thread1 = new MyThread();
thread1.setName("线程1");
MyThread thread2 = new MyThread();
thread2.setName("线程2");
thread1.start();
thread2.start();
}
}
某次运行的结果
cur thread is: 线程1, i is: 0
cur thread is: 线程1, i is: 1
cur thread is: 线程1, i is: 2
cur thread is: 线程1, i is: 3
cur thread is: 线程1, i is: 4
cur thread is: 线程1, i is: 5
cur thread is: 线程1, i is: 6
cur thread is: 线程1, i is: 7
cur thread is: 线程1, i is: 8
cur thread is: 线程1, i is: 9
cur thread is: 线程1, i is: 10
cur thread is: 线程1, i is: 11
cur thread is: 线程1, i is: 12
cur thread is: 线程1, i is: 13
cur thread is: 线程1, i is: 14
cur thread is: 线程1, i is: 15
cur thread is: 线程1, i is: 16
cur thread is: 线程1, i is: 17
cur thread is: 线程1, i is: 18
cur thread is: 线程1, i is: 19
cur thread is: 线程1, i is: 20
cur thread is: 线程1, i is: 21
cur thread is: 线程1, i is: 22
cur thread is: 线程1, i is: 23
cur thread is: 线程1, i is: 24
cur thread is: 线程1, i is: 25
cur thread is: 线程1, i is: 26
cur thread is: 线程1, i is: 27
cur thread is: 线程1, i is: 28
cur thread is: 线程1, i is: 29
cur thread is: 线程2, i is: 0
cur thread is: 线程1, i is: 30
cur thread is: 线程2, i is: 1
cur thread is: 线程1, i is: 31
cur thread is: 线程2, i is: 2
cur thread is: 线程1, i is: 32
cur thread is: 线程2, i is: 3
cur thread is: 线程1, i is: 33
cur thread is: 线程2, i is: 4
cur thread is: 线程1, i is: 34
cur thread is: 线程2, i is: 5
cur thread is: 线程1, i is: 35
cur thread is: 线程2, i is: 6
cur thread is: 线程1, i is: 36
cur thread is: 线程2, i is: 7
cur thread is: 线程1, i is: 37
cur thread is: 线程2, i is: 8
cur thread is: 线程1, i is: 38
cur thread is: 线程2, i is: 9
cur thread is: 线程1, i is: 39
cur thread is: 线程2, i is: 10
cur thread is: 线程1, i is: 40
cur thread is: 线程2, i is: 11
....
使用继承Thread的方式创建线程,缺点就是因为java的单继承机制,使得该类无法再继承其他类。
具体步骤为:
1.定义实现类实现Runnable接口,并实现run方法,run方法中的逻辑即为该线程的具体逻辑。
2.创建Runnable实现类的实例,并以该实例作为Thread的target参数创建Thread对象。
3.调用该Thread对象的start方法启动线程。
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0; i<50; i++) {
String threadName = Thread.currentThread().getName();
System.out.println("cur thread is: " + threadName + ", i is: " + i);
}
}
public static void main(String[] args) {
MyRunnable obj = new MyRunnable();
new Thread(obj, "线程1").start();
new Thread(obj, "线程2").start();
}
}
某次运行结果为:
cur thread is: 线程2, i is: 0
cur thread is: 线程1, i is: 0
cur thread is: 线程2, i is: 1
cur thread is: 线程1, i is: 1
cur thread is: 线程2, i is: 2
cur thread is: 线程1, i is: 2
cur thread is: 线程2, i is: 3
cur thread is: 线程1, i is: 3
cur thread is: 线程2, i is: 4
cur thread is: 线程1, i is: 4
cur thread is: 线程2, i is: 5
cur thread is: 线程1, i is: 5
cur thread is: 线程2, i is: 6
cur thread is: 线程2, i is: 7
...
基本步骤为:
1.创建Callable接口实现类,实现run方法。注意Callable的源码为
@FunctionalInterface
public interface Callable {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
run方法中是后返回类型的。
2.创建Callable实现类的实例对象,并使用Future的实现类FutureTask类包装Callable实例对象。
3.使用FutureTask对象作为Thread对象的target创建并调用start方法启动线程。
4.可以使用FutureTask对象的get方法获取子线程结束执行以后的返回值。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* author: wanglei
* create: 2022-08-18
*/
public class MyCallable implements Callable {
@Override
public Integer call() {
for(int i=0; i<50; i++) {
String name = Thread.currentThread().getName();
System.out.println("子线程" + name + "值为" + i);
}
return 50;
}
public static void main(String[] args) {
MyCallable obj = new MyCallable();
FutureTask task = new FutureTask<>(obj);
for(int j=0; j<50; j++) {
System.out.println("主线程" + Thread.currentThread().getName() + "值为: " + j);
if (j == 10) {
new Thread(task, "有返回值的线程").start();
}
}
try {
System.out.println("子线程返回值: " + task.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
某次运行结果为
/home/mi/wanglei/soft/jdk1.8.0_251/bin/java -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:57127,suspend=y,server=n -javaagent:/home/mi/wanglei/soft/idea-IC-201.7223.91/plugins/java/lib/rt/debugger-agent.jar -Dfile.encoding=UTF-8 -classpath /home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/charsets.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/deploy.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/ext/cldrdata.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/ext/dnsns.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/ext/jaccess.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/ext/jfxrt.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/ext/localedata.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/ext/nashorn.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/ext/sunec.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/ext/sunjce_provider.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/ext/sunpkcs11.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/ext/zipfs.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/javaws.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/jce.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/jfr.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/jfxswt.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/jsse.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/management-agent.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/plugin.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/resources.jar:/home/mi/wanglei/soft/jdk1.8.0_251/jre/lib/rt.jar:/home/mi/wanglei/gitcode/mycoding/target/classes:/home/mi/wanglei/soft/idea-IC-201.7223.91/lib/idea_rt.jar bit.edu.leilei.service.MyCallable
Connected to the target VM, address: '127.0.0.1:57127', transport: 'socket'
主线程main值为: 0
主线程main值为: 1
主线程main值为: 2
主线程main值为: 3
主线程main值为: 4
主线程main值为: 5
主线程main值为: 6
主线程main值为: 7
主线程main值为: 8
主线程main值为: 9
主线程main值为: 10
主线程main值为: 11
主线程main值为: 12
主线程main值为: 13
主线程main值为: 14
主线程main值为: 15
主线程main值为: 16
主线程main值为: 17
主线程main值为: 18
主线程main值为: 19
主线程main值为: 20
主线程main值为: 21
主线程main值为: 22
主线程main值为: 23
主线程main值为: 24
主线程main值为: 25
主线程main值为: 26
主线程main值为: 27
主线程main值为: 28
主线程main值为: 29
主线程main值为: 30
子线程有返回值的线程值为0
主线程main值为: 31
子线程有返回值的线程值为1
主线程main值为: 32
子线程有返回值的线程值为2
主线程main值为: 33
子线程有返回值的线程值为3
主线程main值为: 34
子线程有返回值的线程值为4
主线程main值为: 35
子线程有返回值的线程值为5
主线程main值为: 36
主线程main值为: 37
主线程main值为: 38
主线程main值为: 39
主线程main值为: 40
主线程main值为: 41
主线程main值为: 42
主线程main值为: 43
主线程main值为: 44
主线程main值为: 45
主线程main值为: 46
主线程main值为: 47
主线程main值为: 48
主线程main值为: 49
子线程有返回值的线程值为6
子线程有返回值的线程值为7
子线程有返回值的线程值为8
子线程有返回值的线程值为9
子线程有返回值的线程值为10
子线程有返回值的线程值为11
子线程有返回值的线程值为12
子线程有返回值的线程值为13
子线程有返回值的线程值为14
子线程有返回值的线程值为15
子线程有返回值的线程值为16
子线程有返回值的线程值为17
子线程有返回值的线程值为18
子线程有返回值的线程值为19
子线程有返回值的线程值为20
子线程有返回值的线程值为21
子线程有返回值的线程值为22
子线程有返回值的线程值为23
子线程有返回值的线程值为24
子线程有返回值的线程值为25
子线程有返回值的线程值为26
子线程有返回值的线程值为27
子线程有返回值的线程值为28
子线程有返回值的线程值为29
子线程有返回值的线程值为30
子线程有返回值的线程值为31
子线程有返回值的线程值为32
子线程有返回值的线程值为33
子线程有返回值的线程值为34
子线程有返回值的线程值为35
子线程有返回值的线程值为36
子线程有返回值的线程值为37
子线程有返回值的线程值为38
子线程有返回值的线程值为39
子线程有返回值的线程值为40
子线程有返回值的线程值为41
子线程有返回值的线程值为42
子线程有返回值的线程值为43
子线程有返回值的线程值为44
子线程有返回值的线程值为45
子线程有返回值的线程值为46
子线程有返回值的线程值为47
子线程有返回值的线程值为48
子线程有返回值的线程值为49
子线程返回值: 50
Disconnected from the target VM, address: '127.0.0.1:57127', transport: 'socket'
Process finished with exit code 0
实际工作场景中,使用线程池创建线程较为常见,具体可以参考如下。
直接继承Thread类的方式,实现简单方便,但是因为单继承问题,无法再继承其他类。
解决了单继承问题,但是run方法返回的void类型,无法获取对应的返回值。同时,run方法也无法抛出异常。
1.Callable需要重写call方法,并且call方法可以返回值,同时还可以处理异常。
2.Callable可以返回一个异步的Future对象。通过Future对象可以跟踪线程执行情况,并且可以通过get方法获取线程结果。
实际项目中,一般都是使用线程池进行线程管理。其优点有
1.提升代码性能。因为创建线程是一个比较重的操作,使用线程池可以减少线程创建销毁等的性能开销。
2.可以方便实现定时任务,控制并发数,单线程等功能。