首页 > 其他 > 详细

Runnable和Thread

时间:2021-02-05 01:04:15      阅读:23      评论:0      收藏:0      [点我收藏+]

今天看到有人聊多线程的两种创建方式:实现Runnable和继承Thread,然后讨论这两种方式的优缺点。个人认为这个问题应该不需要讨论,直接推荐实现Runnable方式,至于理由,设计者的设计意图就是这么定的,继承Thread属于骚操作。具体看源码注释。

Runnable接口的注释:

 1 /**
 2  * The <code>Runnable</code> interface should be implemented by any
 3  * class whose instances are intended to be executed by a thread. The
 4  * class must define a method of no arguments called <code>run</code>.
 5  * <p>
 6  * This interface is designed to provide a common protocol for objects that
 7  * wish to execute code while they are active. For example,
 8  * <code>Runnable</code> is implemented by class <code>Thread</code>.
 9  * Being active simply means that a thread has been started and has not
10  * yet been stopped.
11  * <p>
12  * In addition, <code>Runnable</code> provides the means for a class to be
13  * active while not subclassing <code>Thread</code>. A class that implements
14  * <code>Runnable</code> can run without subclassing <code>Thread</code>
15  * by instantiating a <code>Thread</code> instance and passing itself in
16  * as the target.  In most cases, the <code>Runnable</code> interface should
17  * be used if you are only planning to override the <code>run()</code>
18  * method and no other <code>Thread</code> methods.
19  * This is important because classes should not be subclassed
20  * unless the programmer intends on modifying or enhancing the fundamental
21  * behavior of the class.
22  *
23  * @author  Arthur van Hoff
24  * @see     java.lang.Thread
25  * @see     java.util.concurrent.Callable
26  * @since   JDK1.0
27  */

注释说的很明确,如果只是想实现多线程,重写run,那就实现Runnable,如果想定制Thread,如把默认线程优先级改为6,那你就重写Thread。

从面向对象的角度、面向接口编程的角度实现Runnable方式都比继承Thread的方式更合理。

 

再看看具体实现上有什么区别:

//Thread
Thread t = new Thread() {
    public void run() {
        System.out.println("Thread");
    }
};
t.start();

//Runnable
Runnable r=new Runnable() {
    @Override
    public void run() {
        System.out.println("Runnable");
    }
};
Thread t=new Thread(r);
t.start();

en。。。Runnable还多比Thread继承方式多写了几行代码,这不重要,可以通过lamdba减少代码量 new Thread(()-> System.out.println("Runnable")).start(); 

 

可见上边的代码,实现Runnable接口方式的run方法加了@Override,而继承Thread方式的run方法没有加@Override,为什么,因为开发工具没有给出智能提示,甚至继承Thread方式的run方法都是手动敲的,明显影响码字速度嘛,不加@Override又不正规有木有。

 

再看源码:

//实现Runnable方式调用构造函数
public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
}

//继承Thread方式调用构造函数
public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
}

//最终调用初始化函数
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) {
            /* 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();
        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);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

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


//Thread类的run方法
@Override
public void run() {
if (target != null) {
target.run();
}
}

 如上,实现Runnable方式的定义和执行流程是:

创建Runnable类型对象r,创建Thread对象t,把r放到t的target字段中,执行t的run方法,t调用r的run方法。

继承Thread方式的定义和执行流程是:

创建SubThread类型对象t,t定义run方法,隐藏掉父类的run,执行t的run方法。

两者在执行上并没有多大区别,至于说资源共享上就更没什么区别了。

 

最后就是说实现Runnable的方式可以实现继承其他父类,而继承Thread的方式不能在继承其他父类,这就涉及到面向对象的定义了。

比如说有个Compute类,实现了Runnable接口,那么可以说这个类的核心还是计算功能,但是拥有了多线程的能力。但是如果这个Compute类继承自Thread,那么这个Compute类的核心更像是为了完成多线程,而计算功能成了附带品。

 

总是,两种方式都能实现多线程,但还是强烈推荐实现Runnable接口的方式实现多线程吧。

 

Runnable和Thread

原文:https://www.cnblogs.com/jusha/p/14375622.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!