结果:

  线程首先会运行一次,然后抛出java.lang.IllegalThreadStateException异常。

  

根据控制台的异常信息,定位到Thread.java的第708行,也就start()方法内部,打个断点调试:

  

调试发现,第一个次运行start()方法时,threadStatus是0,此时if条件不满足,继续执行,会将当前线程添加到线程组中去执行。

第二次运行start()方法时,threadStatus变成了2,if条件满足,于是抛出了java.lang.IllegalThreadStateException异常。

start()方法的源码:

/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
     让这个线程开始执行。JVM会调用这个线程的run()方法。
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.一个线程多次调用start()方法是非法的。
* In particular, a thread may not be restarted once it has completed
* execution.
*特别说明:一个线程执行完后,不太可能重新运行。
* @exception IllegalThreadStateException if the thread was already
* started.
              如果该线程已经启动,则再次调用start()方法,就会抛出IllegalThreadStateException异常。
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)//新的线程threadState值是0
throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);//通知线程组该线程将要开始运行,这样该线程就会被添加到线程列表中,此时列表的unstarted数将会减少。 boolean started = false;
try {
start0();//调用原生方法态方法启动线程
started = true;//已经运行的标记设置为true
} finally {
try {
if (!started) {//若开始运行标记未设置成功,则通知线程组该线程尝试运行失败
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}

group.add(this)  ---线程组添加线程源码分析

/**
* Adds the specified thread to this thread group.
* 添加特定的线程到该线程组
* <p> Note: This method is called from both library code
* and the Virtual Machine. It is called from VM to add
* certain system threads to the system thread group.
*
* @param t
* the Thread to be added
*
* @throws IllegalThreadStateException
* if the Thread group has been destroyed
*/
void add(Thread t) {
synchronized (this) {//线程同步
if (destroyed) {//如果线程组的销毁标记(destroyed)是true,则抛出IllegalThreadStateException
throw new IllegalThreadStateException();
}
if (threads == null) {//如果线程数组为空,则初始为4个新的线程
threads = new Thread[4];
} else if (nthreads == threads.length) {//如果当前线程组已满,则扩容至原来的2倍
threads = Arrays.copyOf(threads, nthreads * 2);
}
threads[nthreads] = t;//将当前线程放入线程数组的末尾 // This is done last so it doesn't matter in case the
// thread is killed
nthreads++;//线程数组元素个数+1 // The thread is now a fully fledged member of the group, even
// though it may, or may not, have been started yet. It will prevent
// the group from being destroyed so the unstarted Threads count is
// decremented.
nUnstartedThreads--;//未启动线程数-1
}
}

threadStartFailed()源码分析:

/**
* Notifies the group that the thread {@code t} has failed
* an attempt to start.
* 通知线程组该线程尝试运行失败
* <p> The state of this thread group is rolled back as if the
* attempt to start the thread has never occurred. The thread is again
* considered an unstarted member of the thread group, and a subsequent
* attempt to start the thread is permitted.
*
* @param t
* the Thread whose start method was invoked
*/
void threadStartFailed(Thread t) {
synchronized(this) {//同步
remove(t);//从线程组中移除该线程
nUnstartedThreads++;//未启动线程数+1
}
}

结论:

同一个线程只能调用start()方法一次,多次调用会抛出java.lang.IllegalThreadStateException。启动一个线程,需要调用start()方法而不是run()方法。此时,当前线程会被添加到线程组中,进入就绪状态,等待线程调度器的调用,若获取到了资源,则能进入运行状态,run()方法只是线程体,即线程执行的内容,若没调用start()方法,run()方法只是一个普通的方法。

同一个线程多次调用start()会出现的问题的更多相关文章

  1. c/c++ 多线程 多个线程等待同一个线程的一次性事件

    多线程 多个线程等待一个线程的一次性事件 背景:从多个线程访问同一个std::future,也就是多个线程都在等待同一个线程的结果,这时怎么处理. 办法:由于std::future只能被调用一次get ...

  2. Android jni c/c++线程通过CallVoidMethod调用java函数出现奔溃问题

    最近在移植网络摄像机里的p2p库到android平台,需要用到jni,最近在c线程了调用java函数的时候 出现一个问题,假如在同一个线程调用java函数是没问题的,但在一个c线程了调用java函数就 ...

  3. 判断是否在同一个线程-GetCurrentThreadId()用法

    线程 在一个程序中,这些独立运行的程序片断叫作"线程"(Thread),利用它编程的概念就叫作"多线程处理".利用线程,用户可按下一个按钮,然后程序会立即作出响 ...

  4. 解析Qt元对象系统(五) Q_INVOKABLE与invokeMethod(automatic connection从Qt4.8开始的解释已经与之前不同,发送对象驻足于哪一个线程并不重要,起到决定作用的是接收者对象所驻足的线程以及发射信号(该信号与接受者连接)的线程是不是在同一个线程)good

    概述查看Qt源码可知,Q_INVOKABLE是个空宏,目的在于让moc识别. 使用Q_INVOKABLE来修饰成员函数,目的在于被修饰的成员函数能够被元对象系统所唤起. Q_INVOKABLE与QMe ...

  5. https://github.com/python/cpython/blob/master/Doc/library/contextlib.rst 被同一个线程多次获取的同步基元组件

    # -*- coding: utf-8 -*- import time from threading import Lock, RLock from datetime import datetime ...

  6. BeginInvoke 方法真的是新开一个线程进行异步调用吗?

    转自原文BeginInvoke 方法真的是新开一个线程进行异步调用吗? BeginInvoke 方法真的是新开一个线程进行异步调用吗? 参考以下代码: public delegate void tre ...

  7. Java创建线程后,调用start()方法和run()的区别

    1) start方法: 用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码.通过调用Thread类的start()方法来启动一个线程,这时此 ...

  8. java future模式 所线程实现异步调用(转载

    java future模式 所线程实现异步调用(转载) 在多线程交互的中2,经常有一个线程需要得到另个一线程的计算结果,我们常用的是Future异步模式来加以解决.Future顾名思意,有点像期货市场 ...

  9. C# 线程池异步调用

    许多应用程序使用多个线程,但这些线程经常在休眠状态中耗费大量的时间来等待事件发生.其他线程可能进入休眠状态,并且仅定期被唤醒以轮询更改或更新状态信息,然后再次进入休眠状态.为了简化对这些线程的管理,. ...

随机推荐

  1. 基于WebQQ3.0协议写一个QQ机器人

    最近公司需要做个qq机器人获取qq好友列表,并且能够自动向选定的qq好友定时发送消息.没有头绪,硬着头皮上 甘甜的心情瞬间变得苦涩了 哇 多捞吆 1.WEBQQ3.0登陆协议 进入WEBQQ, htt ...

  2. php预定义字符

    Php中预定义字符的处理: 1,htmlspecialchars()把一些预定义的字符转换为 HTML 实体. 函数原型:htmlspecialchars(string,quotestyle,char ...

  3. js身份证掩饰成**(class循环处理)

    $('.shijian').each(function (i){ value=$(this).attr('data');value1=value.replace(/^(.{3})(?:\d+)(.{4 ...

  4. 1.5.7、CDH 搭建Hadoop在安装之前(定制安装解决方案---配置单用户模式)

    配置单用户模式 在传统的Cloudera Manager部署中,管理每台主机上的Hadoop进程的Cloudera Manager Agent以root用户身份运行.但是,某些环境会限制对root帐户 ...

  5. MySql权威指南

    [MySql权威指南] 1.索引(index):原始数据纪录的排序情况. 2.存储过程(store procedure),就是函数. 3.触发器是一组SQL命令,当数据库执行特定操作时触发,如UPDA ...

  6. flashfxp key密钥

    flashfxp3.41中文版注册码:(适合最新版本) 推荐(尚未被封的 Realkey) FLASHFXPvACq2ssbvAAAAAC1W7cJKQTzmx77zmqJICvA7d3WnUtWNX ...

  7. 跨域导致无法获取cookie

    首先我用的框架是vue,请求协议用的是ajax,跨域的处理办法是使用了反向代理,在我之前的博文有详细说明,有兴趣的可以去查看下,在做身份认证权限限制的时候,后台有在http-header的respon ...

  8. java中替换${xx}

    import java.util.regex.Matcher; import java.util.regex.Pattern; public class replace { public static ...

  9. Android四大组件总结

    1:Actiivty 用户可以看见并可以操作的界面 Activity开启方式:startActivityForResult :startActivity Activity生命周期: onCreate  ...

  10. MenOS

    2018-2019-120189224 <庖丁解牛Iinux内核分析>第三周学习总结 MenuOS的构造 下载内核源代码编译内核 cd ~/LinuxKernel/ wget https: ...