Java中Runnable和Thread的区别(网上部分说法是错误的)

在实际工作中,我们很可能习惯性地选择Runnable或Thread之一直接使用,根本没在意二者的区别,但在面试中很多面试官会经常而且非常严肃的问出:请你解释下Runnable或Thread的区别?尤其是新手就容易上当,不知如何回答,就胡乱编一通。鄙人今天告诉你们这二者本身就没有本质区别,就是接口和类的区别。如果非要说区别,请看如下:

  1. Runnable的实现方式是实现其接口即可
  2. Thread的实现方式是继承其类
  3. Runnable接口支持多继承,但基本上用不到
  4. Thread实现了Runnable接口并进行了扩展,而Thread和Runnable的实质是实现的关系,不是同类东西,所以Runnable或Thread本身没有可比性。

  网络上流传的最大的一个错误结论:Runnable更容易可以实现多个线程间的资源共享,而Thread不可以! 这是一个错误的结论!

我们假定一个场景,分3个窗口(线程)买10张票:

一、实现Runnable接口

package com.neonuu.collection.domain;

public class RunnableTest {


    static class MyRunnable implements Runnable{

        //共购买10张票
        private int ticket = 10;

        @Override
        public  void run() {
            //实现Runnable接口
            for(int i=0;i<10;i++){
                //利用synchronized锁,实现同步,防止超卖
                synchronized(this){
                    if(this.ticket>0){
                        try {
                            System.out.println(Thread.currentThread().getName()+"购票前剩余:"+this.ticket);
                            ticket--;
                            System.out.println(Thread.currentThread().getName()+"购票后剩余:"+this.ticket);
                            System.out.println("================");
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else {
                        break;
                    }
                }

            }
        }
    }



    public static void main(String[] args){
        //实现Runnable接口,3个线程同时购买10张票
        MyRunnable myRunable = new MyRunnable();
        Thread myRunnable1 = new Thread(myRunable,"myRunnable1");
        Thread myRunnable2 = new Thread(myRunable,"myRunnable2");
        Thread myRunnable3 = new Thread(myRunable,"myRunnable3");
        myRunnable1.start();
        myRunnable2.start();
        myRunnable3.start();
    }
}

结果如下:

myRunnable1购票前剩余:10
myRunnable1购票后剩余:9
================
myRunnable3购票前剩余:9
myRunnable3购票后剩余:8
================
myRunnable3购票前剩余:8
myRunnable3购票后剩余:7
================
myRunnable2购票前剩余:7
myRunnable2购票后剩余:6
================
myRunnable3购票前剩余:6
myRunnable3购票后剩余:5
================
myRunnable1购票前剩余:5
myRunnable1购票后剩余:4
================
myRunnable1购票前剩余:4
myRunnable1购票后剩余:3
================
myRunnable3购票前剩余:3
myRunnable3购票后剩余:2
================
myRunnable2购票前剩余:2
myRunnable2购票后剩余:1
================
myRunnable2购票前剩余:1
myRunnable2购票后剩余:0
================

二、继承Thread

package com.neonuu.collection.domain;

public class ThreadTest {
    static class MyThread extends Thread {

        //共购买10张票
        private int ticket = 10;

        public void run(){
            //继承Thread类
            for(int i=0;i<10;i++){
                //利用synchronized锁,实现同步,防止超卖
                synchronized(this){
                    if(this.ticket>0){
                        try {
                            System.out.println(Thread.currentThread().getName()+"购票前剩余:"+this.ticket);
                            ticket--;
                            System.out.println(Thread.currentThread().getName()+"购票后剩余:"+this.ticket);
                            System.out.println("================");
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else {
                        break;
                    }
                }

            }
        }

    }

    public static void main(String[] args){

        //方法一:创建3个MyThread实例
        //网上多数采用此方法,来证明Thread与Runnable在资源共享上的区别,其实是错误的
        //结果:3个线程各买了10张票,共30张票
        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();
        MyThread myThread3 = new MyThread();
        myThread1.start();
        myThread2.start();
        myThread3.start();

        //方法二:创建1个MyThread实例,开启3个线程
        //结果:与继承Runnable相同,3个线程同时购买10张票
        MyThread myThread = new MyThread();
        Thread myThread4 = new Thread(myThread,"myThread1");
        Thread myThread5 = new Thread(myThread,"myThread2");
        Thread myThread6 = new Thread(myThread,"myThread3");
        myThread4.start();
        myThread5.start();
        myThread6.start();

    }
}

在main方法中我们使用两种写法

1、方法一的结果:

Thread-0购票前剩余:10
Thread-0购票后剩余:9
Thread-2购票前剩余:10
Thread-1购票前剩余:10
Thread-2购票后剩余:9
================
================
Thread-1购票后剩余:9
================
Thread-1购票前剩余:9
Thread-0购票前剩余:9
Thread-2购票前剩余:9
Thread-1购票后剩余:8
================
Thread-2购票后剩余:8
================
Thread-0购票后剩余:8
================
Thread-0购票前剩余:8
Thread-0购票后剩余:7
================
Thread-2购票前剩余:8
Thread-2购票后剩余:7
================
Thread-1购票前剩余:8
Thread-1购票后剩余:7
================
Thread-2购票前剩余:7
Thread-0购票前剩余:7
Thread-1购票前剩余:7
Thread-0购票后剩余:6
================
Thread-1购票后剩余:6
================
Thread-2购票后剩余:6
================
Thread-0购票前剩余:6
Thread-2购票前剩余:6
Thread-1购票前剩余:6
Thread-2购票后剩余:5
================
Thread-0购票后剩余:5
================
Thread-1购票后剩余:5
================
Thread-1购票前剩余:5
Thread-1购票后剩余:4
================
Thread-0购票前剩余:5
Thread-0购票后剩余:4
================
Thread-2购票前剩余:5
Thread-2购票后剩余:4
================
Thread-2购票前剩余:4
Thread-2购票后剩余:3
================
Thread-0购票前剩余:4
Thread-0购票后剩余:3
================
Thread-1购票前剩余:4
Thread-1购票后剩余:3
================
Thread-1购票前剩余:3
Thread-1购票后剩余:2
================
Thread-0购票前剩余:3
Thread-0购票后剩余:2
================
Thread-2购票前剩余:3
Thread-2购票后剩余:2
================
Thread-2购票前剩余:2
Thread-2购票后剩余:1
================
Thread-0购票前剩余:2
Thread-0购票后剩余:1
================
Thread-1购票前剩余:2
Thread-1购票后剩余:1
================
Thread-2购票前剩余:1
Thread-2购票后剩余:0
================
Thread-0购票前剩余:1
Thread-0购票后剩余:0
================
Thread-1购票前剩余:1
Thread-1购票后剩余:0
================

可以看出,方法一分三个线程分别购买了10张票,共购买了30张。这个结果并不是我们想要的。网上大部分是通过这个来证明Thread不能资源共享,是错误的!

2、方法二的结果

myThread1购票前剩余:10
myThread1购票后剩余:9
================
myThread3购票前剩余:9
myThread3购票后剩余:8
================
myThread2购票前剩余:8
myThread2购票后剩余:7
================
myThread3购票前剩余:7
myThread3购票后剩余:6
================
myThread1购票前剩余:6
myThread1购票后剩余:5
================
myThread3购票前剩余:5
myThread3购票后剩余:4
================
myThread2购票前剩余:4
myThread2购票后剩余:3
================
myThread3购票前剩余:3
myThread3购票后剩余:2
================
myThread1购票前剩余:2
myThread1购票后剩余:1
================
myThread1购票前剩余:1
myThread1购票后剩余:0
================

可以看出,这个结果与Runnable是相同的,3个线程共同购买10张票

注:synchronized这个关键字是必须的,否则会发生超买,买了11张票,ticket变为负数。

三、结论:

事实是Thread和Runnable没有本质的区别,只是写法不同罢了,,这才是正确的结论,与资源共享无关!

 

上一篇:通过实现Runnable接口的方式创建线程


下一篇:定义线程的两种方式