首页 > 其他分享 >Thread使用总结(1)——Runnable和Thread的区别是啥

Thread使用总结(1)——Runnable和Thread的区别是啥

时间:2023-02-14 15:04:05浏览次数:43  
标签:总结 lang Runnable java Thread parent start

问题背景

 在日常安卓开发和学习过程中,我们很可能习惯性地选择Runnable或Thread之一直接使用,那么问题来了,Runnable和Thread的区别是啥?一般来说这二者就是接口和类的区别。比如: (1)Runnable的实现方式是实现其接口即可 (2)Thread的实现方式是继承其类 (3)Runnable接口支持多继承,但基本上用不到 (4)Thread实现了Runnable接口并进行了扩展,而Thread和Runnable的实质是实现的关系,不是同类东西。 我们一起看看网络上流传的结论:Runnable支持多线程间的资源共享,而Thread不可以!我们一起来看看这个结论是怎么得出的呢?

问题分析

首先我们来稍微梳理下Thread相关的部分源码。 1、java.lang.Thread#Thread()

    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

2、java.lang.Thread#init(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long)

    /**
     * Initializes a Thread with the current AccessControlContext.
     * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext)
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null);
    }

3、java.lang.Thread#init(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long, java.security.AccessControlContext)

    /**
     * Initializes a Thread.
     * ...
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        Thread parent = currentThread();
        if (g == null) {
                g = parent.getThreadGroup();
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        // 传进来的runnable对象
        this.target = target;
        init2(parent);

        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

4、java.lang.Thread#init2

    private void init2(Thread parent) {
        this.contextClassLoader = parent.getContextClassLoader();
        this.inheritedAccessControlContext = AccessController.getContext();
        if (parent.inheritableThreadLocals != null) {
            this.inheritableThreadLocals = ThreadLocal.createInheritedMap(
                    parent.inheritableThreadLocals);
        }
    }

5、java.lang.ThreadLocal#createInheritedMap

    /**
     * Factory method to create map of inherited thread locals.
     * Designed to be called only from Thread constructor.
     *
     * @param  parentMap the map associated with parent thread
     * @return a map containing the parent's inheritable bindings
     */
    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

6、java.lang.ThreadLocal.ThreadLocalMap#ThreadLocalMap(java.lang.ThreadLocal.ThreadLocalMap)

        /**
         * Construct a new map including all Inheritable ThreadLocals
         * from given parent map. Called only by createInheritedMap.
         *
         * @param parentMap the map associated with parent thread.
         */
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        // 通过entry数组,也是构造了 <ThreadLocal, value>的映射
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

7、java.lang.Thread#start

    public synchronized void start() {
        if (started)
            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);

        started = false;
        try {
            nativeCreate(this, stackSize, daemon);
            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 */
            }
        }
    }

8、java.lang.Thread#run

    @Override
    public void run() {
        // 传进来的runnable对象
        if (target != null) {
            target.run();
        }
    }

问题解决

现在我们具体来看看网上流传结论得出的有关代码 (1)使用Thread来卖票,三个线程,每个线程都各卖了5张票,代码如下:

fun main() {
    testThread()
}

fun testThread() {
    val thread1 = MyThread()
    val thread2 = MyThread()
    val thread3 = MyThread()
    thread1.start()
    thread2.start()
    thread3.start()
}

class MyThread : Thread() {
    private var ticket = 5
    override fun run() {
        while(true){
            if(ticket <= 0){
                break;
            }
            println(currentThread().toString() + "Runnable ticket = " + ticket--)
        }
    }
}

运行结果如下: image.png 运行结果分析: 每个线程卖了5张,这样一共就卖了15张票了。 (2)使用Runnable来卖票,三个线程,每个线程都各卖了5张票,代码如下:

fun main() {
    testRunnable()
}

fun testRunnable() {
    val mt = MyRunnable()

    val thread1 = Thread(mt)
    val thread2 = Thread(mt)
    val thread3 = Thread(mt)

    thread1.start()
    thread2.start()
    thread3.start()

    thread1.join()
    thread2.join()
    thread3.join()
    println(MyRunnable.count)
}

class MyRunnable: Runnable{
    var ticket = 5
    companion object {
        var count = 0
    }

    override fun run() {
        while(true){
            if(ticket <= 0){
                break;
            }
            println(Thread.currentThread().toString() + "Runnable ticket = " + ticket--)
            count++
        }
    }
}

运行结果如下: image.png 运行结果分析: 三个线程一共卖了5张票。 结果对比分析: 根据上面两个demo的对比呢,网上就流传一个结论,Runnable比Thread的优势,支持多线程间的资源共享? 其实通过刚才Thread类部分源码的梳理,我们可以看到,调用Thread类的start方法后,线程进入相应的就绪状态,等待线程获取cpu时间片之后呢开始运行,调用run()方法,thread的方式实现是继承Runable类,那么demo中三个线程都是分别执行自己重写的run(),每个线程单独内部都有各自的Runnable对象;然后如果是通过传入Runnable的方式,其实三个线程内部的runnable对象都是指向同一个runnable,所以看起来是共享资源的效果,但是所谓的资源共享也可能存在并发上的一些问题,如果要使用这种所谓的共享,需要注意相关同步操作。比如以下demo,就出现并发问题了,代码如下:

fun main() {
    testRunnable()
}

fun testRunnable() {
    val mt = MyRunnable()

    val thread1 = Thread(mt)
    val thread2 = Thread(mt)
    val thread3 = Thread(mt)

    thread1.start()
    thread2.start()
    thread3.start()

    thread1.join()
    thread2.join()
    thread3.join()
    println(MyRunnable.count)
}

class MyRunnable: Runnable{
    var ticket = 10000
    companion object {
        var count = 0
    }

    override fun run() {
        while(true){
            if(ticket <= 0){
                break;
            }
            println(Thread.currentThread().toString() + "Runnable ticket = " + ticket--)
            count++
        }
    }
}

运行结果如下: image.png

问题总结

本文稍微梳理了Runnable和Thread的区别,并对网上流传的“Runnable比Thread的优势,支持多线程间的资源共享”进行了说明和思考,有兴趣的同学可以进一步深入研究。

标签:总结,lang,Runnable,java,Thread,parent,start
From: https://blog.51cto.com/baorant24/6056864

相关文章

  • 2012总结--第1篇--技术篇
    以广度与深度并重为核心指南!1.语言1.1深入学习Java。1.2复习C++,Linux系统下写了几个小程序。大致阅读了一遍《C++Primer》。1.3了解了Python,在Windows平台写了......
  • 2012总结--第9篇--价值观篇
    自己也不小了,转眼间1/3左右的人生已经过去。是该给自己立点规矩,适度地约束自己的言行举止了。无论自己是迷茫时,或是得意时,都能有所选择!1.责任努力奋斗,自立自强!1.1父......
  • 2012总结--第5篇--人脉篇
    综述今年新结识了很多人,友好交往的人还比较多。感觉最缺少的,就是和自己向一个方向走的IT相关的人士。公司很多人也都挺熟悉的,总感觉缺少了一点什么。高中的很多同学,有种......
  • 总结使人进步,遵循事物的发展规律
    雷哥的座右铭:总结使人进步,遵循事物的发展规律。长时间的历练,成功和失败的经验,都证明了这点。 小时候,学得名句“谦虚使人进步”。但,长大后,发现“谦虚”不是最让人进步的最高......
  • Python 科研绘图总结2 一 子图,三维立方体
    Python科研绘图总结2一子图,三维立方体目录Python科研绘图总结2一子图,三维立方体1子图相关函数代码效果2三维图相关函数代码效果3三维动图相关函数代码效果4三维......
  • 前端一面必会vue面试题总结
    Vue模板编译原理Vue的编译过程就是将template转化为render函数的过程分为以下三步第一步是将模板字符串转换成elementASTs(解析器)第二步是对AST进行静态节......
  • 前端二面经典vue面试题总结
    Vue加载流程1.初始化的第一阶段是Vue实例也就是vm对象创建前后:首先Vue进行生命周期,事件初始化发生在beforeCreate生命周期函数前,然后进行数据监测和数据代理的初始化,也就......
  • 2022/02/13--开学考总结
    我甚至找不出我写的东西的优点,那来总结下思路吧。思路:首先,应该对项目需求了解。对需求分析,然后建数据库的表,分析有三部分:评论为一个板块,新闻为一个板块,人的板块。其次,应......
  • 【工作】【春节后找工作】找工作注意事项【10年经验总结】【防骗技巧】【避坑】
    春节过后有大批求职者,也有潜伏在深处的非人力HR,如何鉴别防骗?总结如下: ......
  • python pandas库总结-数据分析和操作工具
    参考:https://pandas.pydata.org/Input/output相关函数pandas.read_excel—将Excel文件读入pandas数据框支持读取xls,xlsx,xlsm,xlsb,odf,ods和odt文件扩展名,支持单......