浅谈线程002-线程创建方式

目前java创建线程存在4中方式:
1: 通过继承 Thread 类本身;
2: 通过实现 Runnable 接口;
3: 通过 Callable 和 FutureTask 创建线程
4: 使用线程池方式

一、通过继承Thread类,并重写run方法实现线程创建

//方式1:继承Thread实现自定义线程
public class MyThread extends  Thread{

    //重写父类run方法,实现线程执行逻辑
    public void run() {
        System.out.println("myThead线程执行了.....");
    }
}
public class App {
    public static void main(String[] args) {
        //创建线程对象
        MyThread thread = new MyThread();
        //调用start方法启动线程
        thread.start();
        System.out.println("这是主线程.....");
    }
}

1:必须继承Thread父类
2:重写run方法,否则是空实现
3:启动线程是使用start() 方法而不是run(),  如果thread.run(), 仅仅表示对象调用方法,并不是多线程操作。

二、通过实现Runable接口创建线程

//方式2:通过实行Runnable接口创建线程
public class MyRunnable implements Runnable {
    //线程执行逻辑
    public void run() {
        System.out.println("线程:" + Thread.currentThread().getName()+" 执行了...");
    }
}
public class App {

    public static void main(String[] args) {

        //创建接口实现类
        MyRunnable run = new MyRunnable();

        //参数1:Runnable接口实例
        //参数2:线程名字
        Thread thread = new Thread(run, "线程1");

        //调用start方法启动线程
        thread.start();
        System.out.println("这是主线程.....");
    }
}

注意
1: 必须要实现Runnable接口,重写run方法
2: 线程运行需要借助Thread(Runnable run, String ThreadName) 构造器
3: 如果线程不需要多次运行,可以使用匿名内部类实现

public class App2 {

    public static void main(String[] args) {
        //参数1:Runnable接口实例
        //参数2:线程名字
        Thread thread = new Thread(new Runnable(){
            //线程执行逻辑
            public void run() {
                System.out.println("线程:" + Thread.currentThread().getName()+" 执行了...");
            }
        }, "线程1");

        //调用start方法启动线程
        thread.start();
        System.out.println("这是主线程.....");
    }
}

分析
其实方式1是方式2的一种变种,本质上还是方式2, 可以打开Thread源码, 发现Thread类也实现了Runnable接口

public class Thread implements Runnable {...}

当new Thread(Runnable, threadName) 时,底层做了这个操作

public Thread(Runnable target, String name) {
        init(null, target, name, 0);
}
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
....
//在Thread类中也维护了一个Runnable 对象
this.target = target;
....
}

当thread调用start方法时,实际上调用本地方法,让操作系统开辟线程

public synchronized void start() {
....
    start0();
....
}
private native void start0();

最后回调执行run方法,就发现底层实际上是执行Runnable方法

    public void run() {
        if (target != null) {
            target.run();
        }
    }

三、通过 Callable 和 FutureTask 创建线程

//方式3:使用Callable跟FuntrueTask方式实现
public class MyCallable implements Callable<String> {
    //线程执行逻辑
    public String call() throws Exception {
        System.out.println("线程:" + Thread.currentThread().getName() + ",执行....");
        //5s之后返回结果
        Thread.sleep(5000);
        return "线程执行完后的返回值";
    }
}
public class App3 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //创建Callable接口实现类对象
        MyCallable callable = new MyCallable();

        //创建FutureTask对象
        FutureTask ft = new FutureTask(callable);

        //将FutureTask对象对象作为参数传入
        Thread thread = new Thread(ft, "线程1");

        //调用start方法启动线程
        thread.start();

        //获取线程执行完毕返回的值
        System.out.println(ft.get());
        System.out.println("这是主线程.....");
    }
}

注意
1: 必须要Callable 接口,重写call方法, 接口泛型机试call方法返回值类型
2: 需要与FutrueTask对象协同使用
3: FutrueTask 使用可以与Thread对象协同使用,也可以跟线程池配合使用
4: 可以调用FutrueTask对象中的get方法获取线程执行返回值

分析
方式3跟方式1,方式2最大区别在于方式3线程执行完之后可以通过回到方法get返回线程执行结果。那么,它是怎么做的的呢。

 看上图,FutrueTask类实现了Runnable接口,该类也持有一个Callable的属性,目的用于接收传入的Callable对象。同时重写了run方法,在run方法中调用了callable接口的call方法,得到返回,而call方法,就是线程逻辑执行方法。此时定义一个变量outcome保存执行结果,那么完全可以通过get方法线程执行结果。再看会操作代码

 public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建Callable接口实现类对象
        MyCallable callable = new MyCallable();
        //创建FutureTask对象
        FutureTask ft = new FutureTask(callable);
        //将FutureTask对象对象作为参数传入
        Thread thread = new Thread(ft, "线程1");
        //调用start方法启动线程
        thread.start();
        //获取线程执行完毕返回的值
        System.out.println(ft.get());
        System.out.println("这是主线程.....");
    }

得到结果,其实方法3也是方式2的一种变种。本质还是一样的。

四、通过线程池方式

可以使用线程池方式来构建线程

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
public class Test {
	public static void main(String[] args) {
		ExecutorService ex=Executors.newFixedThreadPool(5);
		
		for(int i=0;i<5;i++) {
			ex.submit(new Runnable() {
				
				@Override
				public void run() {
					for(int j=0;j<10;j++) {
						System.out.println(Thread.currentThread().getName()+j);
					}
					
				}
			});
		}
		ex.shutdown();
	}	
}

线程工作了原理:

 从这看,线程池更多是对多个线程进行流程控制,本质还是方式1,方式2或者方式3,并没有离开线程本质。

总结:
1: 如果线程类已经继承了其他类,那么创建线程就需要使用Runnable接口/FutureTask方式
2: 如果需要返回值则需要FutrueTask方式
3: 如果需要使用线程池,可以使用Runnable跟FutrueTask方式。
4: 如果需要多线程管理,使用线程池的方式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浪飞yes

我对钱没兴趣~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值