Java线程:start方法与run方法区别、Thread与Runnable关系、处理线程返回值与线程状态讲解

一、Thread的start与run方法的区别

1.1 t.run()的时候

从运行结果可以知道,main里面当前的线程与attck里面的线程是一样的

public class ThreadTest {
    private static void attack(){
        System.out.println("Fight");
        System.out.println("Current Thread is : "+Thread.currentThread().getName());
    }
    public static void main(String[] args){
        //线程
        Thread t = new Thread(){
            public void run(){
                attack();
            }
        };
        //打印现在的主线程
        System.out.println("Current main thread is : "+Thread.currentThread().getName());
        t.run();
    }
}

在这里插入图片描述

1.2 t.start()的时候

从运行结果可以知道,main函数的线程为main,而attack里面的线程为Thread-0

public class ThreadTest {
    private static void attack(){
        System.out.println("Fight");
        System.out.println("Current Thread is : "+Thread.currentThread().getName());
    }
    public static void main(String[] args){
        //线程
        Thread t = new Thread(){
            public void run(){
                attack();
            }
        };
        //打印现在的主线程
        System.out.println("Current main thread is : "+Thread.currentThread().getName());
        t.start();
    }
}

在这里插入图片描述

1.3 比较与分析

通过1.1与1.2的比较,当我们使用run的时候,会延用主线程运行方法,而使用start的时候,则会使用一个非main的线程来运行方法。

Thread-0是通过start方法来创建的,查看start的源码,发现主要方法为private native void start0(),native会调用外部一些非java的源码,查看网站http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/1e8cdf311133/src/share/native/java/lang/Thread.c可找到start0的源码,发现是关于JVM的,如下图标注:

在这里插入图片描述

查看网站http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/760b28d87178/src/share/vm/prims/jvm.h找到关于jvm相关内容,查找关键字jvm_start,发现它是会创建一个新的线程,如下图标注:

在这里插入图片描述

期间传入了thread_entry,查找thread_entry,可以看到它会call虚拟机并传入run方法的名字,即它会创建一个新的线程去执行我们run方法里的内容,如下图所示:

在这里插入图片描述

通过比较,java线程中的start()会调用JVM_StartThread创建一个新的子线程,然后通过thread_entry中的run方法去调用run(),区别即:调用strat()方法会创建一个新的子线程并启动,run()方法只是Thread的一个普通方法的调用

二、Thread和Runnable的关系

2.1 Thread是一个类,Runnable是一个接口

Thread实现Runnable接口,使得run支持多线程,而Runnable接口非常简单,只有一个抽象的run方法,即Runnable接口并没有多线程的特性,它是依赖Thread前面讲的start方法去创建子线程,再在子线程里面调用这个Thread的run方法,因类的单一继承原则,推荐多使用Runnable接口

2.2 Thread实现多线程
public class MyThread extends Thread{
    private String name;
    public MyThread(String name){
        this.name = name;
    }
    @Override
    public void run(){
        for(int i = 0; i < 10; i++){
            System.out.println("Thread start : " + this.name+" , i = "+i);
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args){
        MyThread mt1 = new MyThread("Thread1");
        MyThread mt2 = new MyThread("Thread2");
        MyThread mt3 = new MyThread("Thread3");
        mt1.start();
        mt2.start();
        mt3.start();
    }
}

由运行结果可以看到,发现线程是交互执行,顺序不确定的,达到了多线程的目的

在这里插入图片描述

2.3 Runnable实现多线程

查看Thread可以发现到一个构造方法,即证明Runnable依赖Thread

public Thread(Runnable target) {
        this(null, target, "Thread-" + nextThreadNum(), 0);
    }
public class MyRunnable implements Runnable {
    private String name;

    public MyRunnable(String name){
        this.name=name;
    }

    @Override
    public void run() {
        for(int i = 0; i < 10; i++){
            System.out.println("Thread start : " + this.name+" , i = "+i);
        }
    }
}
public class RunnableDemo {
    public static void main(String[] args) {
        MyRunnable mr1 = new MyRunnable("Runnable1");
        MyRunnable mr2 = new MyRunnable("Runnable2");
        MyRunnable mr3 = new MyRunnable("Runnable3");
        Thread t1 = new Thread(mr1);
        Thread t2 = new Thread(mr2);
        Thread t3 = new Thread(mr3);
        t1.start();
        t2.start();
        t3.start();
    }
}

由运行结果可以看到,发现线程是交互执行,顺序不确定的,达到了多线程的目的

在这里插入图片描述

三、处理线程的返回值

3.1 实现方式

根据线程相关的业务逻辑,需要放在run()方法里面执行,而run方法是没有参数,也没有返回值的,如下:

public abstract void run();

传参可以通过构造函数传参成员变量传参回调函数传参,但多数情况下,有些程序的执行是依赖子任务的执行去进行的,实现返回值便可以通过主线程等待法,即让主线程循环等待子线程结束Thread类的join()阻塞当前线程以等待子线程处理完毕,即阻塞主线程,等待join里面的子线程完成为止通过Callable接口实现,通过FutureTask或者线程池,即可以获取Future对象,在这个对象上调用get就可以获取Callable任务无返回的Object

3.2 主线程等待法例子
3.2.1 返回值为null

根据之前讲的run与start例子中知道,当主线程main运行到t.start()的时候的时候,运行CycleWait的子线程还没给value赋值的时候,主线程main便打印了value,所以值为null

public class CycleWait implements Runnable{
    private String value;
    @Override
    public void run() {
        try {
            //让当前线程等待5秒后再执行
            Thread.currentThread().sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        value = "We find a kinda wonderland now!";
    }
    public static void main(String[] args){
        CycleWait cycleWait = new CycleWait();
        Thread t = new Thread(cycleWait);
        t.start();
        System.out.println(cycleWait.value);
    }
}
3.2.2 修改代码,让主线程等待子线程给value赋值完毕

主线程等待方法实现简单,但是需要自己实现等待逻辑,例如下面的while方法,在项目中如果等待的子线程较多,代码便会显得臃肿,循环多久还无法精确低控制

public class CycleWait implements Runnable{
    private String value;
    @Override
    public void run() {
        try {
            //让当前线程等待5秒后再执行
            Thread.currentThread().sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        value = "We find a kinda wonderland now!";
    }
    public static void main(String[] args) throws InterruptedException {
        CycleWait cycleWait = new CycleWait();
        Thread t = new Thread(cycleWait);
        t.start();
        //让主线程等待
        while(cycleWait.value==null){
            Thread.currentThread().sleep(100);
        }
        System.out.println(cycleWait.value);
    }
}
3.3 join方法阻塞主线程

使用方便,比主线程等待发更加精准低控制,但join方法的力度不够细,例如以上面循环输出i为例子,当我们想Runnable1中i=5的时候启动Runnable2,join便无法实现

public class CycleWait implements Runnable{
    private String value;
    @Override
    public void run() {
        try {
            //让当前线程等待5秒后再执行
            Thread.currentThread().sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        value = "We find a kinda wonderland now!";
    }
    public static void main(String[] args) throws InterruptedException {
        CycleWait cycleWait = new CycleWait();
        Thread t = new Thread(cycleWait);
        t.start();
        //join阻塞
        t.join();
        System.out.println(cycleWait.value);
    }
}
3.4 实现Callable接口
3.4.1 Callable接口
//Callable接口,call方法返回一个泛型
@FunctionalInterface
public interface Callable<V> {
    /**
     * 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;
}
3.4.2 FutureTask类主要方法

FutureTask实现RunnableFuture接口,RunnableFuture接口实现Runnable接口,所以3.4.4的task传入Thread

    //构造函数可以接受Callable实现类的实例
	public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
	//用来判断传来的Callable实现类的call的方法是否已经执行完成
    public boolean isDone() {
        return state != NEW;
    }
	//用来阻塞当前调用它的线程,直到callable实现类的call方法实现完毕为止
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
	//含有timeout的get方法,加入超时机制,如果在超时的时间内还没获取call的值,就抛出超时异常
    public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        if (unit == null)
            throw new NullPointerException();
        int s = state;
        if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
        return report(s);
    }
3.4.3 MyCallable.java
public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        String value ="test";
        System.out.println("I am ready to work!");
        //等待5秒
        Thread.currentThread().sleep(5000);
        System.out.println("task done!");
        return value;
    }
}
3.4.4 FutureTaskDemo,通过FutureTask获取返回值
public class FutureTaskDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //futuretask传入callable
        FutureTask<String>  task = new FutureTask<String>(new MyCallable());
        new Thread(task).start();
        if(!task.isDone()){
            System.out.println("task has not finished,plz wait");
        }
        //task.get就会获取到MyCallable的return value
        System.out.println("task return : "+task.get());
    }
}
3.5 通过线程池获取返回值
public class ThreadPoolDemo {
    public static void main(){
        //创建线程池
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        //提交mycallable任务,返回的是future对象
        Future<String> future = newCachedThreadPool.submit(new MyCallable());
        if(!future.isDone()){
            System.out.println("task has not finished,plz wait");
        }
        try {
            System.out.println("task return : "+future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }finally {
            newCachedThreadPool.shutdown();
        }
    }
}
3.6 future和线程池结果

“task done”等字符串会等待MyCallable5秒后return value后才显示,如下图所示:

在这里插入图片描述

四、线程的状态

  • new:新建,创建后尚未启动的线程的状态,即新创建一个线程对象,可是没有调用start方法

  • Runnable:运行、包含Running和Ready,有可能正在执行也有可能在等待CPU分配时间,例如调用了start方法后便处于Runnable状态,处于Running的状态的线程在可运行的线程池, 处于Ready的状态的线程在线程池中,等待被调度选中,分配CPU使用时间

  • Waiting:无限期等待,不会被分配CPU执行时间,需要显示被唤醒,需要被其他线程显示地唤醒

    • 没有设置Timeout参数的Object.wait()方法
    • 没有设置Timeout参数的Thread.join()方法
    • LockSupport.park()方法
  • Timed Waiting:限期等待,在一定时间后会由自动唤醒,不需要被其他线程显示地唤醒

    • Thread.sleep()方法
    • 设置了Timeout参数的Object.wait()方法
    • 设置了Timeout参数的Thread.join()方法
    • LockSupport.parkNanos()方法
    • LockSupport.parkUntil()方法
  • Blocked:阻塞,等待获取排它锁

  • Terminated:已终止线程的状态,线程已经结束执行,其时如果再调用则会报错

要显示被唤醒,需要被其他线程显示地唤醒

  • 没有设置Timeout参数的Object.wait()方法
  • 没有设置Timeout参数的Thread.join()方法
  • LockSupport.park()方法
  • Timed Waiting:限期等待,在一定时间后会由自动唤醒,不需要被其他线程显示地唤醒
    • Thread.sleep()方法
    • 设置了Timeout参数的Object.wait()方法
    • 设置了Timeout参数的Thread.join()方法
    • LockSupport.parkNanos()方法
    • LockSupport.parkUntil()方法
  • Blocked:阻塞,等待获取排它锁
  • Terminated:已终止线程的状态,线程已经结束执行,其时如果再调用则会报错
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值