前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Thread源码解析

Thread源码解析

作者头像
写一点笔记
发布2020-08-25 14:28:40
3060
发布2020-08-25 14:28:40
举报
文章被收录于专栏:程序员备忘录程序员备忘录

我们上学的时候都知道线程有两种方式,要么继承Thread类,要么实现runable接口。根据我们上次对线程池的分析,发现我们对Thread类的理解还比较浅显。所以深入理解Thread成为我们以后学习线程池的基础。

代码语言:javascript
复制
实现Runnable接口方式
public class ShowMessage implements Runnable{

    private String message="任务";
    @Override
    public void run() {
        System.out.println(message);
    }

    public static void main(String[] args) {
        Thread thread=new Thread(new ShowMessage());
        thread.start();
    }
}


继承方式
public class MyThread extends Thread{

    @Override
    public void run() {
        System.out.println("继承的任务");
    }

    public static void main(String[] args) {
        new MyThread().start();
    }
}

通过上述的例子我们大致的信息是Runnable的run是线程执行的核心。那么到底是这样吗?我们先看一下Thread类和Runnable接口的关系,在没有查看源代码的前提下我个人觉得Thread肯定是实现了Runnable接口的。

而通过查看Runnable接口定义的方法,我们发现其实Runnable接口只有一个方法那就是run方法。

那么我们是不是可以得出的结论:thread类相当于一个工具类,专门用来将执行runnable的任务,当然由于Thread自身实现了Runnable接口所以它本身内部也可以直接定义任务。那么问题就变成了Thread如何开辟线程并进行运行的了,但是相对于高级语言来说它肯定是调用的操作系统提供的api了,所以说我们前进的步法可能就止于逻辑的合理性上了。

在上边的Thread类的结构图上,我们看到类的基本结构。我们回顾一下我们上一文中的ThreadPoolExecutor的任务队列,通过我们的分析,难么任务队列也必然就是一系列实现了Runnable接口的任务,而Thread类就全盘交付给了ThreadFactory了。其实分析到这里,我们基本上都不用看Thread类就可以去尝试看ThreadPoolExecutor的实现了。可是既然我们来了那就多多少少看看Thread类吧,毕竟Thread类维护线程的状态还是比较麻烦的。

根据类的实例化过程我们首先注意到的是registerNatives方法,这是native方法显然这块应该是将这个线程注册到哪里?根据一般规则,肯定是操作系统了。目前来说就这样理解吧。

代码语言:javascript
复制
    private static native void registerNatives();
    static {
        registerNatives();
    }

我们看到所有的初始化方法最终都调用的init方法

代码语言:javascript
复制
    private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        this.name = name;
        //当前线程
        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            if (security != null) {
                g = security.getThreadGroup();
            }
            //设置默认线程组
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }


        g.checkAccess();
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }


        g.addUnstarted();


        //设置线程组
        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =acc != null ? acc : AccessController.getContext();
        //设置真正的任务
        this.target = target;
        //设置线程的优先级
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        //设置线程的栈空间大小
        this.stackSize = stackSize;


        //设置线程id
        tid = nextThreadID();
    }

但是其中最重要的方法是native,所以parent就是已经创建好的线程了。

代码语言:javascript
复制
public static native Thread currentThread();

看到上述代码已经将线程所需要的限制条件进行设置,那么下面的方法就是在条件范围内玩逻辑了

那么我们看看start()方法内在的实现逻辑。

代码语言:javascript
复制
public synchronized void start() {
        //如果线程的状态不是新建的就抛出异常,因为start肯定是新的线程状态也就是0了
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        //上边在初始化的过程中,我们发现只是进行线程组的赋值,而不是添加,真正添加到线程组的操作是在启动线程的时候进行的
        group.add(this);
        boolean started = false;
        try {
        //调用的native方法启动线程
            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 */
            }
        }
    }
   //剔除启动失败的线程
    void threadStartFailed(Thread t) {
        synchronized(this) {
            remove(t);
            nUnstartedThreads++;
        }
    }

但是启动线程的时候我们并没有发现那个方法调用了run方法,显然是start0在native层面上掉用的run方法。而run方法的本质是runnable接口。所以任务和线程是分开的。也验证了我们上述的说法。

代码语言:javascript
复制
 private void exit() {
        if (group != null) {
            group.threadTerminated(this);
            group = null;
        }
        /* Aggressively null out all reference fields: see bug 4006245 */
        target = null;
        /* Speed the release of some of these resources */
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }

线程中断的时候是通过加锁的方式进行的,防止多个并发线程去竞争中断其他线程,interrupt0也是调用的native方法。

代码语言:javascript
复制
   public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();


        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }
代码语言:javascript
复制
获取存活线程的数量
public static int activeCount() {
        return currentThread().getThreadGroup().activeCount();
    }

//在join方法中,采用wait阻塞的方式让线程在指定时间内休眠

代码语言:javascript
复制
    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;


        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }


        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

在线程优先级上

代码语言:javascript
复制
    //最小优先级
    public final static int MIN_PRIORITY = 1;


    //默认的优先级
    public final static int NORM_PRIORITY = 5;


    //最大的优先级
    public final static int MAX_PRIORITY = 10;

线程的五种状态

新建:当我们new Thread()就相当于创建了一个线程,这个线程向下进行注册,那么这个线程就处于在编模式,但并没有启动。那么这个这样的状态叫做新建。

就绪:当线程调用start0方法并执行玩start方法返回成功,但并没有执行run方法中的代码片段。而且线程有很多,但是cpu的核数是有限的。所以没有执行run方法的线程称之为就绪。 通过我们在start方法的学习,如果start成功了那么线程组就将这个线程添加到线程组,否则就剔除掉。那么是否可以说明在线程组中注册的线程都曾经历过就绪态?

运行:当线程获得cpu时间片段并执行run方法的时候称之为运行态。

阻塞:正在运行run方法的线程由于某种原因让出cpu让其他就绪态的线程使用cpu资源。

死亡:run方法执行结束或者run方法抛出了异常没有被处理,并跳出了run方法导致的线程猝死。另外isAlive方法可以用来判断线程是否是活着的,在就绪、阻塞和运行态时候会返回true,其他状态返回false。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员备忘录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档