前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java多线程(一)快速认识线程

java多线程(一)快速认识线程

原创
作者头像
3号攻城狮
发布2018-08-26 12:40:30
2730
发布2018-08-26 12:40:30
举报

背景

一般来讲,被问到线程的时候我们都能简单的来几句。

问:实现一个线程有几种方式?

答:继承Thread类,实现Runnable接口,为了展示自己学识的渊博,还可能还会说实现Callable接口通过FutureTask包装器来创建Thread线程,或则说通过线程池来运行一个线程。

问: 继承Thread类和实现Runnable接口有什么区别呢?

答: ...............

一般而言如果平时不太关注这一块的东西,基本上也就停留在这一块了,但是一旦使用到该方面的知识,要用的时候,临时恶补,少不了的是对整个并发体系的理解不够透彻,最终在设计,代码编写的时候少不了的要走不少弯路。

已经有各种书籍博客对Java多线程进行教学及总结了,本系列更多的是帮助博主本人总结,记录,提高,如果在该过程中还能帮助到其他小伙伴,那就真是太开心了。

概念

线程:进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

线程生命周期

232002051747387.jpg
232002051747387.jpg
代码语言:txt
复制
新建状态(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()方法,该线程结束生命周期。

继承Thread类

代码语言:txt
复制
 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();
        }
    }

实现Runnable接口

代码语言:txt
复制
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类圆满我们可以很清晰的找到答案。

代码语言:txt
复制
public class Thread implements Runnable 

首先Thread类本身是实现Runnable接口的。

代码语言:txt
复制
public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
}

在Thread类中有构造方法如上,用来接收Runnable 的实例对象。那么我们获取到了Runnble实例后最终是如何使用的呢?

代码语言:txt
复制
  @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

见到此方法,那么就明了了。实现Runnable 接口,最终传递给Thread类,最终的作用只是用来更改线程的实现单元。那么我们经常使用的start方法又是用来做什么的呢?

代码语言:txt
复制
 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 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 概念
    • 线程生命周期
      • 继承Thread类
        • 实现Runnable接口
        • 分析
        • 总结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档