常用创建线程的两种方式(3)

概要

线程作为独立调度运行和分派的基本单位,很明显的一个特征便是可运行的。Runnable接口便是对可被调度运行进行了抽象声明。本章主要内容如下:

  • 线程构造函数介绍
  • Thread和Runnable的区别与联系
  • 创建线程示例

线程构造函数介绍

    /**
     * 无参构造函数,常用
     */
    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * 需要参数Runnable对象构造函数,常用
     */
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    Thread(Runnable target, AccessControlContext acc) {
        init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
    }

    public Thread(ThreadGroup group, Runnable target) {
        init(group, target, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * 线程名称,常用
     */
    public Thread(String name) {
        init(null, null, name, 0);
    }

    public Thread(ThreadGroup group, String name) {
        init(group, null, name, 0);
    }

    /**
     * 常用
     */
    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }

创建线程的时候常用无参和设置线程名、设置Runnable对象及组合的方式。常见的有Thread()、Thread(Runnable target)、Thread(String name)和Thread(Runnable target, String name),设置线程组的方式比较少见,除非真的有必要进行分组管理,系统启动的时候会设置系统线程组、main线程组。

线程init初始化方法

java.lang.Thread

    /**
     * Initializes a Thread.
     *
     * @param g 线程组
     * @param target 可被调度运行对象
     * @param name 线程名
     * @param stackSize 线程堆栈大小,0表示忽略
     * @param acc 线程内部访问控制
     * @param inheritThreadLocals 是否继承线程本地变量
     */
    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();
        //默认情况下jvm不开启安全管理器
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                //不是通过设置线程组的方式创建线程,默认归属于父线程的线程组
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();
        //线程组赋值
        this.group = g;
        //是否守护线程继承默认继承自父线程
        this.daemon = parent.isDaemon();
        //线程优先级,也是继承自父线程,默认为5
        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();
        //Runnable实例,相当于要运行的task
        this.target = target;
        setPriority(priority);
        //继承父线程inheritThreadLocals
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

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

除了安全管理器的访问检查稍微陌生(自行baidu、google),线程初始化相对比较简单,做了线程相关属性的初始化操作。

Thread和Runnable的区别与联系

Runnable是接口,用于声明Runnable实例是可以被线程调度运行的,只定义了一个run()方法:

public interface Runnable {
    public abstract void run();
}

可以通过public Thread(Runnable target)、public Thread(Runnable target, String name)构造函数进行创建线程。
Thread是类,实现了Runnable接口,这也说明了线程是可以被独立调度运行和分派的。

public class Thread implements Runnable {
    ...

    /**
     * 实现Runnable,线程CPU被调度后代码执行入口
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

    ...
}

从代码上看,直接通过无参构造函数创建线程并启动,实际上run()就相当于一个空方法。因此,如果不以Runnable实例创建线程,则应该通过继承Thread类覆盖run()方法的方式创建线程。接下来是常见的创建线程的两种方式。

创建线程示例

  • 继承Thread类覆盖run()方法的方式
public class Hello extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " Hello word !");
    }
}
//调用
public class ThreadCreate {
    public static void main(String[] args) {
        //启动三个线程
        for (int i = 0; i < 3; i++) {
            //一定要注意的是调用start()方法启动线程,而非调用run()方法
            new Hello().start();
        }
    }
}

运行结果:

Thread-0 Hello word !
Thread-2 Hello word !
Thread-1 Hello word !

  • 实现Runnable方式创建线程
    上边的代码修改为实现Runnable方式:
public static class Hello implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " Hello word !");
    }
}

public class ThreadCreate {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(new Hello()).start();
        }
    }
}

运行结果:

Thread-0 Hello word !
Thread-2 Hello word !
Thread-1 Hello word !

选择哪种方式

通过继承的方式,Thread实现Runnable接口,即Thread是Runnable实例。从Thread实现Runnable接口代码片段看,如果target(Runnable实例)为空,run()相当于是一个空方法,继承覆盖的方式实际上就是重新实现了Runnable接口。
target被多个线程共享,同一个任务由多个线程完成,发挥多线程的优势。拿Thread当Runnable实例感觉上总是有些别扭。
所以,从面向接口编程的角度看推荐直接实现Runnable的方式创建线程。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,548评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,497评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,990评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,618评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,618评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,246评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,819评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,725评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,268评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,356评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,488评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,181评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,862评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,331评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,445评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,897评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,500评论 2 359

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,696评论 18 139
  • 进程和线程 进程 所有运行中的任务通常对应一个进程,当一个程序进入内存运行时,即变成一个进程.进程是处于运行过程中...
    小徐andorid阅读 2,814评论 3 53
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,964评论 1 18
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,458评论 1 15
  • 一个好的游戏会让人感到轻松愉快,是排解压力的方式,有的游戏能让人收获友谊,有的游戏能够提高某种能力,比如逻辑思维、...
    Molly_0阅读 426评论 0 0