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!