一般来讲,被问到线程的时候我们都能简单的来几句。
问:实现一个线程有几种方式?
答:继承Thread类,实现Runnable接口,为了展示自己学识的渊博,还可能还会说实现Callable接口通过FutureTask包装器来创建Thread线程,或则说通过线程池来运行一个线程。
问: 继承Thread类和实现Runnable接口有什么区别呢?
答: ...............
一般而言如果平时不太关注这一块的东西,基本上也就停留在这一块了,但是一旦使用到该方面的知识,要用的时候,临时恶补,少不了的是对整个并发体系的理解不够透彻,最终在设计,代码编写的时候少不了的要走不少弯路。
已经有各种书籍博客对Java多线程进行教学及总结了,本系列更多的是帮助博主本人总结,记录,提高,如果在该过程中还能帮助到其他小伙伴,那就真是太开心了。
线程:进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
class MyThread extends Thread {
private int index = 0;
@Override
public void run() {
for (index = 0; index < 100; index++) {
System.out.println(Thread.currentThread().getName() + " " + index);
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
class MyThread implements Runnable {
private int index = 0;
@Override
public void run() {
for (index = 0; index < 100; index++) {
System.out.println(Thread.currentThread().getName() + " " + index);
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
}
}
从以上两个例子我们可以看到,继承Thread也好,实现Runnable接口也好,最终启动线程的时候都是通过
Thread类的start方法。
翻阅Thread类圆满我们可以很清晰的找到答案。
public class Thread implements Runnable
首先Thread类本身是实现Runnable接口的。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
在Thread类中有构造方法如上,用来接收Runnable 的实例对象。那么我们获取到了Runnble实例后最终是如何使用的呢?
@Override
public void run() {
if (target != null) {
target.run();
}
}
见到此方法,那么就明了了。实现Runnable 接口,最终传递给Thread类,最终的作用只是用来更改线程的实现单元。那么我们经常使用的start方法又是用来做什么的呢?
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)
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);
boolean started = false;
try {
start0();
started = 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 */
}
}
}
private native void start0();
如源码所述,start方法最终是用来调用本地方法,启动线程的。
通过源码阅读,我们知道继承Thread类重写run方法,还是实现Runnable类重写run方法,最终的作用是编写线程的执行单元,真正创建一个线程的是start方法。同理我们可以猜测,在开头提到的其他创建线程的方式,溯源的话都应该是通过Thread类的start方法来启动一个线程,只不过是封装的程度不一样。
一个简单的认识过程,于我们的学习来说,起到了一个入门的作用,接下来的一个系列我会通过我重零接触Java多线程的过程做一个简单分享。小伙伴们可以通过我不好的经验总结更加完善的知识体系。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。