天天看点

Java 并发编程之Runnable和Thread实现多线程的区别

Java中实现多线程通常有两种方式(其实从Java5开始有三种了,第三种先不说,ps:

Java 并发编程之Runnable和Thread实现多线程的区别

我暂时还没去研究):

1.继承Thread类

2.实现Runnable接口

虽说有两种实现方式,但是很明显在实际开发中实现Runnable这种方式明显要比继承Thread多多了,这是因为Runnable拥有天生的优势:

1.在多线程访问同一资源的情况下,用Runnable接口创建的线程可以处理同一资源,而用Thread创建的线程则独自处理,各自拥有自己的资源。

2.避免了Java单继承带来的局限性。

第二条优势实在太明显了,咱们这里不谈

Java 并发编程之Runnable和Thread实现多线程的区别

,咱们举一个烂大街的例子来说明下第一条优势。

首先通过继承Thread实现,上代码:

public class MyThread extends Thread {
    private int mTicket = 5;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (mTicket > 0) {
                System.out.println(Thread.currentThread().getName() + "----mTicket = " + mTicket--);
            }
        }

    }
}


public class ThreadDemo {
    public static void main(String arg0[]) {
        ArrayList<Thread> threads = new ArrayList<>();

        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();
        MyThread myThread3 = new MyThread();

        threads.add(myThread1);
        threads.add(myThread2);
        threads.add(myThread3);

        for (Thread thread : threads) {
            thread.start();
        }


        try {
            for (Thread thread : threads) {
                thread.join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();

        }

        System.out.println("所有线程执行完毕!");

    }
}
           

运行结果:

Thread-0----mTicket = 5
Thread-2----mTicket = 5
Thread-2----mTicket = 4
Thread-2----mTicket = 3
Thread-2----mTicket = 2
Thread-2----mTicket = 1
Thread-1----mTicket = 5
Thread-1----mTicket = 4
Thread-0----mTicket = 4
Thread-0----mTicket = 3
Thread-0----mTicket = 2
Thread-0----mTicket = 1
Thread-1----mTicket = 3
Thread-1----mTicket = 2
Thread-1----mTicket = 1
所有线程执行完毕!
           

咱们开启了三个线程,每个线程各自卖了5张票,它们之间互不干扰很和谐。但是现实生活中其实并不是这样的,火车站官网售票是需要多个线程共同完成一个任务的,继承Thread这种方式实现不了,咱们试试实现Runnable接口这种方式,上代码:

public class MyRunnable implements Runnable {
    private int mTicket = 5;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (mTicket > 0) {
                System.out.println(Thread.currentThread().getName() + "----mTicket = " + mTicket--);
            }
        }
    }
}


public class ThreadDemo {
    public static void main(String arg0[]) {
        ArrayList<Thread> threads = new ArrayList<>();

        MyRunnable runnable = new MyRunnable();

        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        Thread thread3 = new Thread(runnable);


        threads.add(thread1);
        threads.add(thread2);
        threads.add(thread3);

        for (Thread thread : threads) {
            thread.start();
        }


        try {
            for (Thread thread : threads) {
                thread.join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();

        }

        System.out.println("所有线程执行完毕!");

    }
}
           

第一次运行结果:

Thread-1----mTicket = 4
Thread-1----mTicket = 3
Thread-1----mTicket = 2
Thread-1----mTicket = 1
Thread-0----mTicket = 5
所有线程执行完毕!
           

第二次运行结果:

Thread-0----mTicket = 5
Thread-0----mTicket = 3
Thread-0----mTicket = 2
Thread-0----mTicket = 1
Thread-2----mTicket = 4
Thread-1----mTicket = 5
所有线程执行完毕!
           

为何出现两种不同的结果呢,因为3个Thread对象共同执行Runnable对象中的代码,线程执行的时机又是难以预测的,很有可能会造成线程不安全。这时只要加上同步锁,确保同一时刻只有一个线程在执行for循环里的代码,就可以保证线程安全了。咱们修改下MyRunnable的代码,上代码:

public class MyRunnable implements Runnable {
    private int mTicket = 5;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized (this) {
                if (mTicket > 0) {
                    System.out.println(Thread.currentThread().getName() + "----mTicket = " + mTicket--);
                }
            }

        }
    }
}
           

第一次运行结果:

Thread-0----mTicket = 5
Thread-0----mTicket = 4
Thread-0----mTicket = 3
Thread-0----mTicket = 2
Thread-0----mTicket = 1
所有线程执行完毕!
           

第二次运行结果:

Thread-0----mTicket = 5
Thread-0----mTicket = 4
Thread-1----mTicket = 3
Thread-1----mTicket = 2
Thread-1----mTicket = 1
所有线程执行完毕!
           

嘿嘿,不管运行多少次,发现结果都是一样的。