上一篇我们初步认识了线程,现在我们来讲一下,创建线程的三种方式
1.继承Thread
类通过继承thread类,然后重写run方法(run方法中是线程真正执行的代码,runable也是如此)即可。当子类被实例化后,调用start方法即可启动线程。(不能调用run方法,直接调用run方法,和方法调用没有区别,不是线程。)
这种继承thread创建线程的方式,线程之间无法共享实例变量,因为每次都创建了新的实例,肯定不能共享了。
/**
* 通过集成Thread类,重写run方法启动线程
*/
public class FirstThread extends Thread {
private int i;
public void run() {
for (; i < 100; i++) {
//当线程类继承thread类时,直接使用this即可获得当前线程
//Thread对象的getName(),返回当前线程的名字
//因此可以直接调用getName()方法返回当前线程的名字
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
//调用thread的currentThread()方法获取当前线程
System.out.println(Thread.currentThread().getName() + " " + i);
if(i==20){
//创建并启动第一个线程
new FirstThread().start();
//创建并启动第二个线程
new FirstThread().start();
//类似Runable的启动方法,通过继承thread也可以。
FirstThread firstThread1 = new FirstThread();
new Thread(firstThread1,"thread线程").start();
}
}
}
}
执行效果大致如下:
有四个线程启动,一个主线程,线程名字为main 0。还有三个其他线程,线程名分别为:Thread-0、Thread-1、thread线程。这四个线程都打印了1到100。运行结果的一部分展示如下:
2.实现Runable接口
顾名思义,我们需要创建一个类,由此类实现runable接口,然后重写run方法。在启动线程时,将类实例化,然后创建thread,然后将实例化得对象作为创建thread的的参数即可。这种方式创建的线程类,是互相共享资源的。
/**
* 通过实现runable接口来创建线程类
*/
public class SecondThread implements Runnable {
private int i;
//run方法同样是线程执行体
public void run() {
for (; i < 100; i++) {
//当线程类实现runable接口时
//如果想获取当前线程,只能用Thread.currentThread()方法
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(Target,name)方法创建新线程
new Thread(st,"新线程1").start();
new Thread(st,"新线程2").start();
}
}
}
}
通过runable来创建线程是共享实例变量的,故执行结果是i是被共享的,不是新线程1和新线程2 各自执行打印1到100,而是拿到上一线程修改后的变量,继续打印。执行部分效果如下:

其实类似的还有一种创建方式,是通过lambda表达式进行创建的了,原理是和实现runable接口是一样的。
public class T02_CreateThreadByLambda {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
if (i == 20) {
for (int j = 0; j < 100; j++) {
new Thread(() -> {
System.out.println("Hello Lambda!");
}).start();
}
}
}
}
}
执行的部分结果如下:由图可知,我们这种创建方式也是可以,其实lambda表达式,就相当于创建thread的参数,然后启动线程。

3.使用Callable和Future创建线程
从继承Thread类和实现Runnable接口可以看出,上述两种方法都不能有返回值,且不能声明抛出异常。而Callable接口则实现了此两点,Callable接口如同Runable接口的升级版,其提供的call()方法将作为线程的执行体,同时允许有返回值。
但是Callable对象不能直接作为Thread对象的参数,因为Callable接口是 Java 5 新增的接口,不是Runnable接口的子接口。对于这个问题的解决方案,就引入 Future接口,此接口可以接受call() 的返回值,RunnableFuture接口是Future接口和Runnable接口的子接口,可以作为Thread对象的target 。并且, Future 接口提供了一个实现类:FutureTask 。
FutureTask实现了RunnableFuture接口,可以作为 Thread对象的target。
/**
* 使用callable和future创建线程
*/
public class ThirdThread {
public static void main(String[] args) {
//创建Callable对象
ThirdThread rt = new ThirdThread();
//先使用Lambda表达式创建Callable<Interger>对象
//使用furureTask来 包装Callable对象
FutureTask<Integer> task = new FutureTask<>((Callable<Integer>) () -> {
int i = 0;
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
//call方法可以有返回值
return i;
});
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 20) {
//实质还是以callable对象来创建并启动线程的
new Thread(task, "有返回值的线程").start();
}
}
try {
//获取线程返回值
System.out.println("子进程的返回值:" + task.get());
}
catch (Exception ex){
ex.printStackTrace();
}
}
}
执行结果我们我们可以猜测一下,应该是有名称叫“main”线程打印0到99,名称叫“有返回值的线程”打印0到99,最后是子进程,由于最后还进行了一次++,所以返回值为100,所以就打印了一次:“子进程的返回值:100”
执行的部分结果如下:

中间都是打印“有返回值的线程:i” ,我省略掉了。

4.使用线程池创建
1)使用Executors类中的newFixedThreadPool(int num)方法创建一个线程数量为num的线程池
2)调用线程池中的execute()方法执行由实现Runnable接口创建的线程;调用submit()方法执行由实现Callable接口创建的线程
3)调用线程池中的shutdown()方法关闭线程池
public class Thread4 {
public static void main(String[] args) throws Exception {
Thread.currentThread().setName("主线程");
System.out.println(Thread.currentThread().getName()+":"+"输出的结果");
//通过线程池工厂创建线程数量为2的线程池
ExecutorService service = Executors.newFixedThreadPool(2);
//执行线程,execute()适用于实现Runnable接口创建的线程
service.execute(new ThreadDemo4());
service.execute(new ThreadDemo6());
service.execute(new ThreadDemo7());
//submit()适用于实现Callable接口创建的线程
Future<String> task = service.submit(new ThreadDemo5());
//获取call()方法的返回值
String result = task.get();
System.out.println(result);
//关闭线程池
service.shutdown();
}
}
//实现Runnable接口
class ThreadDemo4 implements Runnable{
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+"输出的结果");
}
}
//实现Callable接口
class ThreadDemo5 implements Callable<String>{
@Override
public String call() throws Exception {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+"输出的结果");
return Thread.currentThread().getName()+":"+"返回的结果";
}
}
//实现Runnable接口
class ThreadDemo6 implements Runnable{
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+"输出的结果");
}
}
//实现Runnable接口
class ThreadDemo7 implements Runnable{
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+"输出的结果");
}
}
输出结果如下:

本文详细介绍了在Java中创建线程的四种常见方法:继承Thread类、实现Runnable接口、使用Callable与Future以及利用线程池。每种方法都有其适用场景和优缺点,如资源共享、返回值支持等。

3456

被折叠的 条评论
为什么被折叠?



