Thread和Runnable关于共享资源的对比,网上看到很多不正确的结论如下:
Thread类创建多线程,无法保证多个线程对共享资源的正确操作,而Runnable接口可以保证多个线程对共享资源的正确访问。
得到这个结论的原因如下:
ThreadDemo
- package org.example.bk.multithread;
-
-
- public class ThreadDemo {
- public static void main(String[] args) {
- // 1 看起来重复卖票
- new TicketWindow().start(); // 创建并开启第一个线程对象
- new TicketWindow().start(); // 创建并开启第二个线程对象
- }
- }
-
- class TicketWindow extends Thread {
- private int tickets = 5;
- public void run() {
- while (tickets > 0) {
- Thread th = Thread.currentThread(); // 获取当前线程
- String th_name = th.getName(); // 获取当前线程的名字
- System.out.println(th_name + " 正在发售第 " + tickets-- + " 张票 ");
- }
- }
- }
运行结果

从结果中看到每张票都都卖了2次,出现了"重复卖票"的情况。
RunnableDemo
- package org.example.bk.multithread;
-
-
- public class RunnableDemo {
- public static void main(String[] args) {
- TicketWindow1 tw = new TicketWindow1();
- new Thread(tw, "窗口1").start(); // 创建线程对象并命名为窗口1,开启线程
- new Thread(tw, "窗口2").start(); // 创建线程对象并命名为窗口2,开启线程
- }
- }
-
- class TicketWindow1 implements Runnable {
- private int tickets = 5;
- public void run() {
- while (tickets > 0) {
- Thread th = Thread.currentThread(); // 获取当前线程
- String th_name = th.getName(); // 获取当前线程的名字
- System.out.println(th_name + " 正在发售第 " + tickets-- + " 张票 ");
- }
- }
- }
-
运行结果

从结果来看,每张票只卖了1次。没有"重复卖票"的现象。
1. 从源码层面看Thread和Runnable,Thread实现了Runnable接口,实现多线程的本质还是Runnable接口。

2.造成"重复卖票"和"不重复卖票"的原因是什么呢?
"重复卖票"的原因分析:
ThreadDemo:new了两次TicketWindows对象,所以两个TickeWindow对象里面的tickets也是各自的变量,不是同一资源,自然在多线程时各自卖各自的tickets,自然能看到"重复卖票"的现象。
- package org.example.bk.multithread;
-
-
- public class ThreadDemo {
- public static void main(String[] args) {
- new TicketWindow().start(); // 创建并开启第一个线程对象
- new TicketWindow().start(); // 创建并开启第二个线程对象
- }
- }
-
- class TicketWindow extends Thread {
- private int tickets = 5;
- public void run() {
- while (tickets > 0) {
- Thread th = Thread.currentThread(); // 获取当前线程
- String th_name = th.getName(); // 获取当前线程的名字
- System.out.println(th_name + " 正在发售第 " + tickets-- + " 张票 ");
- }
- }
- }
"不重复卖票"的原因分析:
RunnableDemo:只new了TicketWindow1对象1次。所以1个对象里的tickets只有1个变量,所以在多线程卖票时共享这个变量,自然不会出现"重复卖票"的现象。
- package org.example.bk.multithread;
-
-
- public class RunnableDemo {
- public static void main(String[] args) {
- TicketWindow1 tw = new TicketWindow1();
- new Thread(tw, "窗口1").start(); // 创建线程对象并命名为窗口1,开启线程
- new Thread(tw, "窗口2").start(); // 创建线程对象并命名为窗口2,开启线程
- }
- }
-
- class TicketWindow1 implements Runnable {
- private int tickets = 5;
- public void run() {
- while (tickets > 0) {
- Thread th = Thread.currentThread(); // 获取当前线程
- String th_name = th.getName(); // 获取当前线程的名字
- System.out.println(th_name + " 正在发售第 " + tickets-- + " 张票 ");
- }
- }
- }
-
这里举反例进行说明
让Thread不重复卖票,让Runnable重复卖票。
ThreadDemo2
- package org.example.bk.multithread;
-
-
- public class ThreadDemo2 {
- public static void main(String[] args) {
- TicketWindow tw = new TicketWindow();
- Thread t1 = new Thread(tw,"窗口1");
- Thread t2 = new Thread(tw,"窗口2");
- t1.start();
- t2.start();
- }
- }
-
- class TicketWindow extends Thread {
- private int tickets = 5;
- public void run() {
- while (tickets > 0) {
- Thread th = Thread.currentThread(); // 获取当前线程
- String th_name = th.getName(); // 获取当前线程的名字
- System.out.println(th_name + " 正在发售第 " + tickets-- + " 张票 ");
- }
- }
- }
-
运行结果

从结果看到Thread方式可以"不重复卖票"。因为只new TicketWindow对象1次。
RunnableDemo2
- package org.example.bk.multithread;
-
-
- public class RunnableDemo2 {
- public static void main(String[] args) {
- TicketWindow1 tw1 = new TicketWindow1();
- TicketWindow1 tw2 = new TicketWindow1();
- Thread t1 = new Thread(tw1,"窗口1");
- Thread t2 = new Thread(tw2,"窗口2");
- t1.start();
- t2.start();
- }
- }
-
- class TicketWindow1 implements Runnable {
- private int tickets = 5;
- public void run() {
- while (tickets > 0) {
- Thread th = Thread.currentThread(); // 获取当前线程
- String th_name = th.getName(); // 获取当前线程的名字
- System.out.println(th_name + " 正在发售第 " + tickets-- + " 张票 ");
- }
- }
- }
-
运行结果

从运行结果看,Runnable也可以实现"重复卖票",因为new TicketWindow1对象2次。
"重不重复卖票"现象并不是Thread和Runnable的不同造成的,而是由于创建线程的方式不同造成的。
完成!enjoy it!