精华内容
下载资源
问答
  • 线程调用start方法是立即执行了吗 ? 面试者回答: 线程不是马上执行的;准确来说,调用start( )方法后,线程的状态从 new 的状态 变成 “READY(就绪)”状态,而不是“RUNNING(运行中)”状态(关于线程的状态详细...

    面试官问:
    线程调用start方法是立即执行了吗 ?

    面试者回答:
    线程不是马上执行的;准确来说,调用start( )方法后,线程的状态从 new 的状态 变成 “READY(就绪)”状态,而不是“RUNNING(运行中)”状态(关于线程的状态详细,可参考 java中的线程状态)。线程要等待CPU调度,不同的JVM有不同的调度算法,线程何时被调度是未知的。因此,start()方法的被调用顺序不能决定线程的执行顺序

    参考
    https://www.cnblogs.com/jinggod/p/8485143.html

    展开全文
  • 线程对象调用run方法不开启线程。仅是对象调用方法线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行。

    线程对象调用run方法不开启线程。仅是对象调用方法。线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行。

    展开全文
  • 同一个线程对象能否多次调用start方法,搞清楚这个问题,首先需要了解线程的生命周期 一、线程生命周期 更多线程状态细节描述可查看Thread内部枚举类:State 从上图线程状态转换图可以看出: 新建(NEW)状态是...

    同一个线程对象能否多次调用start方法,搞清楚这个问题,首先需要了解线程的生命周期

    一、线程生命周期

    线程生命周期
    更多线程状态细节描述可查看Thread内部枚举类:State
    从上图线程状态转换图可以看出:

    1. 新建(NEW)状态是无法通过其他状态转换而来的;
    2. 终止(TERMINATED)状态无法转为其他状态。

    为何新建状态和终止状态不可逆转,接下来将通过Thread源码来分析

    二、先通过一个正常程序来了解线程的执行过程:

    public static void main(String[] args) {
        //创建一个线程t1
        Thread t1 = new Thread(() -> {
            try {
                //睡眠10秒,防止run方法执行过快,线程组被销毁
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        //第一次启动
        t1.start();
    }
    
    1. 当执行new Thread时,Thread构造方法会调用内部init方法做一些初始化工作,如设置线程组、目标方法、线程名称、堆栈大小、线程优先级等,线程状态是由volatile关键字修饰的threadStatus控制的,初始值为0,即0表示新建状态(NEW);
    2. 调用t1.start方法后,该方法会将调用本地方法start0,start0会创建一个新线程并修改Thread.threadStatus的值;

    扩展:了解Java线程的start方法如何回调run方法

    下面看下start方法源码:

    /**线程成员变量,默认为0,volatile修饰可以保证线程间可见性*/
    private volatile int threadStatus = 0;
    /* 当前线程所属的线程组 */
    private ThreadGroup group;
    /**
     * 同步方法,同一时间,只能有一个线程可以调用此方法
     */
    public synchronized void start() {
        //threadStatus
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        //线程组
        group.add(this);
        boolean started = false;
        try {
            //本地方法,该方法会实际调用run方法
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    //创建失败,则从线程组中删除该线程
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* start0抛出的异常不用处理,将会在堆栈中传递 */
            }
        }
    }
    
    1. 通过断点跟踪,可以看到当线程对象第一次调用start方法时会进入同步方法,会判断threadStatus是否为0,如果为0,则进行往下走,否则抛出非法状态异常;
    2. 将当前线程对象加入线程组;
    3. 调用本地方法start0执行真正的创建线程工作,并调用run方法,可以看到在start0执行完后,threadStatus的值发生了改变,不再为0;
    4. finally块用于捕捉start0方法调用发生的异常。

    扩展:线程是如何根据threadStatus来判断线程的状态的呢?
    通过查看Thread提供的public方法getState可以看到,调用的是sun.misc.VM.toThreadState(threadStatus),根据位运算得出线程的不同状态:

    public static State toThreadState(int var0) {
            if ((var0 & 4) != 0) {
                return State.RUNNABLE;
            } else if ((var0 & 1024) != 0) {
                return State.BLOCKED;
            } else if ((var0 & 16) != 0) {
                return State.WAITING;
            } else if ((var0 & 32) != 0) {
                return State.TIMED_WAITING;
            } else if ((var0 & 2) != 0) {
                return State.TERMINATED;
            } else {
                return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
            }
        }
    

    继续回到原话题,当start调用后,并且run方法内容执行完后,线程是如何终止的呢?实际上是由虚拟机调用Thread中的exit方法来进行资源清理并终止线程的,看下exit方法源码:

    /**
     * 系统调用该方法用于在线程实际退出之前释放资源
     */
    private void exit() {
        //释放线程组资源
        if (group != null) {
            group.threadTerminated(this);
            group = null;
        }
        //清理run方法实例对象
        target = null;
        /*加速资源释放。快速垃圾回收 */
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }
    
    1. 到这里,t1 线程经历了从新建(NEW),就绪(RUNNABLE),运行(RUNNING),定时等待(TIMED_WAITING),终止(TERMINATED)这样一个过程;
    2. 由于在第一次 start 方法后,threadStatus 值被改变,因此第二次调用start时会抛出非法状态异常;
    3. 在调用start0方法后,如果run方法体内容被快速执行完,那么系统会自动调用exit方法释放资源,销毁对象,所以第二次调用start方法时,有可能内部资源已经被释放。

    初步结论:同一个线程对象不可以多次调用 start 方法。

    三、通过反射修改threadStatus来多次执行start方法

    public static void main(String[] args) throws Exception {
        //创建一个线程t1
        Thread t1 = new Thread(() -> {
            try {
                //睡眠10秒,防止run方法执行过快,
                //触发exit方法导致线程组被销毁
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    
        //第一次启动
        t1.start();
    
        //修改threadStatus,重新设置为0,即 NEW 状态
        Field threadStatus = t1.getClass().getDeclaredField("threadStatus");
        threadStatus.setAccessible(true);
        //重新将线程状态设置为0,新建(NEW)状态
        threadStatus.set(t1, 0);
    
        //第二次启动
        t1.start();
    }
    

    截取start后半截源码:

    boolean started = false;
    try {
        //第二次执行start0会抛异常,这时started仍然为false
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                //创建失败,则从线程组中删除该线程
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* start0抛出的异常不用处理,将会在堆栈中传递 */
        }
    }
    
    1. 在上面代码中,在第一次调用start方法后,我通过反射修改threadStatus值,这样在第二次调用时可以跳过状态值判断语句,达到多次调用start方法;
    2. 当我第二次调用t1.start时,需要设置run方法运行时间长一点,防止系统调用exit方法清理线程资源;
    3. 经过以上两步,我成功绕开 threadStatus 判断和线程组增加方法,开始执行start0方法,但是在执行start0的时候抛出异常,并走到了finally块中,由于start为false,所以会执行group.threadStartFailed(this)操作,将该线程从线程组中移除;
    4. 所以start0中还是会对当前线程状态进行了一个判断,不允许重复创建线程。

    最后结论:无论是直接二次调用还是通过反射二次调用,同一个线程对象都无法多次调用start方法,仅可调用一次。

    参考:麦田-了解Java线程的start方法如何回调run方法

    展开全文
  • 面试过程中经常会被面试官问到为什么我们调用start()方法时会执行run()方法,为什么不能直接调用run()方法,问的一头雾水,今天小编给大家介绍下Java线程start方法回调run方法的操作技巧,需要的朋友参考下吧
  • 线程多次调用start方法,引发的异常

    千次阅读 2018-01-16 10:16:47
    简单的线程使用,大家应该都会使用,下面的这个问题,不知道大家有没有注意过。 public class Test146 { public static void main(String[] args) { MyThread11 m = new MyThread11(); for(int i = 0;i&lt...

    简单的线程使用,大家应该都会使用,下面的这个问题,不知道大家有没有注意过。

    public class Test146 {
        public static void main(String[] args) {
            MyThread11 m = new MyThread11();
            for(int i = 0;i<5;i++) {
                m.start();
            }
    
        }
    }
    
    class MyThread11 extends Thread{
              @Override
            public void run() {
                // TODO Auto-generated method stub
                super.run();
                System.out.println("我是线程任务");
            }
    }
    

    运行结果如下:

    这里写图片描述

    接下来,我们就要看这个异常是如何产生的。网上对此解释,说是在循环的时候,共用了一个Thread对象。可是为什么共用了一个Thread对象,就引发错误了呢。

           private volatile int threadStatus = 0;
           A zero status value corresponds to state "NEW".
           if (threadStatus != 0)
                throw new IllegalThreadStateException();
    

    我们都知道,线程的五种生命周期, 新建->就绪->运行->死亡->堵塞。在每次实例化的时候,会给这个threadStatus 为0,用volatile ,可以保证该变量的可见性。在不同的生命周期,值会变,而在start方法通过判断,就可以知道执行的线程是不是一个新的线程实例。

    由上面的,我们可以看出如果我们保证每次都使用的不是同一个Thread实例,就可以解决上面的问题。我们试试创建线程的另一个方法,实现runnable。每一个任务都是包装在一个Thread实例中,所以,执行循环任务,不会出现上述的问题。

    代码如下:

            Thread t;
            for(int i = 0;i<5;i++) {
             t = new Thread(new MyTask11());//MyTask11内容和MyThread11一致,就是它是通过implement实现Thread,不是继承。
             t.start();
    展开全文
  • 线程连续两次调用start()方法

    千次阅读 2020-02-27 18:37:07
    示例代码 public class ..."线程当前的状态为" ..."start后线程当前的状态为" ...可以发现,第一次调用start()方法能够有结果,而第二次则...线程连续两次调用start()方法,会抛出IllegalThreadStateException异常。
  • 了解Java线程start方法如何回调run方法

    千次阅读 多人点赞 2017-11-07 19:53:45
    Java 创建线程方法实际上,创建线程最重要的是提供线程函数(回调函数),该函数作为新创建线程的入口函数,实现自己想要的功能。Java 提供了两种方法来创建一个线程:继承 Thread 类class MyThread extends ...
  • 或者在java线程调用start方法与run方法的区别在哪里? 这两个问题是两个非常流行的初学者级别的多线程面试问题。当一个Java程序员开始学习线程的时候,他们首先会学着去继承Thread类,重载run方法或者实现Runnable...
  • 启动一个线程调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行,这并不意味着线程就会立即运行。run()方法线程启动后要进行回调(callback)的方法。 ...
  • Java中启动一个线程为何调用start()方法而不是run()方法? 那么首先看一个例子: class MyThread extends Thread{ @Override public void run() { System.out.println("直接继承Thread"); } } public class Test...
  • 面试官经常问到这个问题:“创建线程后 在main方法中直接调用run()和调用start()方法有什么区别?”   区别在于:1.直接调用run()方法 根本就没有使创建的线程执行,也就是说 ...2,调用start方法,jvm就直
  • 线程启动会通过调用start方法来启动线程而不能直接调用run方法。 这里就会引出两个经典的面试题: 为什么线程启动是调用start方法来启动线程而不能直接调用run方法? 如果多次调用start方法会发生什么? 其实...
  • 这里有一道经典的面试题:“一个线程两次调用start()方法会出现什么情况?谈谈线程的生命周期和状态转移。” 我们就以这道题目为切入点深入聊聊线程吧。 典型回答 Java的线程是不允许启动两次的,第二次调用必然会...
  •  start()方法启动线程后,整个线程处于就绪状态,等待虚拟机调度, 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此...
  • 多线程在工作中多多少少会用到,我们知道启动多线程调用的是 start() 方法,而不是 run() 方法,你知道原因吗? 在探讨这个问题之前,我们先来了解一些多线程的基础知识~ 线程的状态 Java 中,定义了 6 种线程状态,...
  • 大家好,我是 Oracle首席工程师...今天我要问你的问题是,一个线程两次调用start()方法会出现什么情况?谈谈线程的生命周期和状态转移。   典型回答 Java的线程是不允许启动两次的,第二次调用必然会抛出Illeg...
  • package com; public class Test extends Thread { public void run(){  //定义线程的run()方法 for(int i=0;i //定义循环语句 System.out.print(i+"\t"); //输出循环变量 }
  • 为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?...**顺便说下,类A继承了Tread类,在A中写run方法,就会覆盖掉Thread中的run方法,所以此时调用start方法后,实现的是自己的ru...
  • 启动线程肯定是要用start()方法。当用start开始一个线程后,线程就进入了就绪状态,使线程所代表的...start()是方法,它调用run()方法,而run()方法是你必须重写的,run()方法中包含的是线程的主体。  ...
  • 但是在主函数中直接调用就没问题,但是如果在创建线程的时候作为参数调用方法就会出问题,错误提示是“应输入方法名称”。求各位大神解惑,先谢过! 错误代码是 Thread th1 = new Thread(new ThreadStart(H.显示...
  • public class MyTest { public static void main(String[] args) ... System.out.println("main方法调用method,得到了结果"+result+":"+new Date().toLocaleString()); } public static String method(){ .
  • 同一个线程对象是不能多次调用start函数开辟线程 (例)以下代码的12行会报错,虽然12行代码执行时第一次开辟线程运行的run函数已经结束 代码: 1) public class MyThread extends Thread { 2) @Override 3) ...
  • C# 多线程调用静态方法或者静态实例中的同一个方法-方法内部的变量是线程安全的       using System;using System.Threading;using System.Threading.Tasks;using System.Diagnostics;using System....
  • 今天在看高洪岩老师的Java多线程编程核心技术是看到有个注意点:一个线程调用两次start()方法,会出现Exception in thread “main” java.lang.IllegalThreadStateException的错误。我不知道为什么于是去网上查了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 527,351
精华内容 210,940
关键字:

线程调用start方法