• Java多线程之Thread和Runnable关于共享资源的对比


    背景

    Thread和Runnable关于共享资源的对比,网上看到很多不正确的结论如下:

    Thread类创建多线程,无法保证多个线程对共享资源的正确操作,而Runnable接口可以保证多个线程对共享资源的正确访问。

    得到这个结论的原因如下:

    ThreadDemo 

    1. package org.example.bk.multithread;
    2. public class ThreadDemo {
    3. public static void main(String[] args) {
    4. // 1 看起来重复卖票
    5. new TicketWindow().start(); // 创建并开启第一个线程对象
    6. new TicketWindow().start(); // 创建并开启第二个线程对象
    7. }
    8. }
    9. class TicketWindow extends Thread {
    10. private int tickets = 5;
    11. public void run() {
    12. while (tickets > 0) {
    13. Thread th = Thread.currentThread(); // 获取当前线程
    14. String th_name = th.getName(); // 获取当前线程的名字
    15. System.out.println(th_name + " 正在发售第 " + tickets-- + " 张票 ");
    16. }
    17. }
    18. }

    运行结果

     从结果中看到每张票都都卖了2次,出现了"重复卖票"的情况。

    RunnableDemo 

    1. package org.example.bk.multithread;
    2. public class RunnableDemo {
    3. public static void main(String[] args) {
    4. TicketWindow1 tw = new TicketWindow1();
    5. new Thread(tw, "窗口1").start(); // 创建线程对象并命名为窗口1,开启线程
    6. new Thread(tw, "窗口2").start(); // 创建线程对象并命名为窗口2,开启线程
    7. }
    8. }
    9. class TicketWindow1 implements Runnable {
    10. private int tickets = 5;
    11. public void run() {
    12. while (tickets > 0) {
    13. Thread th = Thread.currentThread(); // 获取当前线程
    14. String th_name = th.getName(); // 获取当前线程的名字
    15. System.out.println(th_name + " 正在发售第 " + tickets-- + " 张票 ");
    16. }
    17. }
    18. }

    运行结果

     从结果来看,每张票只卖了1次。没有"重复卖票"的现象。

    其实这种现象并不是Thread和Runnable的不同造成的,而是由于创建线程的方式不同造成的。

    1. 从源码层面看Thread和Runnable,Thread实现了Runnable接口,实现多线程的本质还是Runnable接口。

    2.造成"重复卖票"和"不重复卖票"的原因是什么呢?

    "重复卖票"的原因分析:

    ThreadDemo:new了两次TicketWindows对象,所以两个TickeWindow对象里面的tickets也是各自的变量,不是同一资源,自然在多线程时各自卖各自的tickets,自然能看到"重复卖票"的现象。

    1. package org.example.bk.multithread;
    2. public class ThreadDemo {
    3. public static void main(String[] args) {
    4. new TicketWindow().start(); // 创建并开启第一个线程对象
    5. new TicketWindow().start(); // 创建并开启第二个线程对象
    6. }
    7. }
    8. class TicketWindow extends Thread {
    9. private int tickets = 5;
    10. public void run() {
    11. while (tickets > 0) {
    12. Thread th = Thread.currentThread(); // 获取当前线程
    13. String th_name = th.getName(); // 获取当前线程的名字
    14. System.out.println(th_name + " 正在发售第 " + tickets-- + " 张票 ");
    15. }
    16. }
    17. }

    "不重复卖票"的原因分析:

    RunnableDemo:只new了TicketWindow1对象1次。所以1个对象里的tickets只有1个变量,所以在多线程卖票时共享这个变量,自然不会出现"重复卖票"的现象。

    1. package org.example.bk.multithread;
    2. public class RunnableDemo {
    3. public static void main(String[] args) {
    4. TicketWindow1 tw = new TicketWindow1();
    5. new Thread(tw, "窗口1").start(); // 创建线程对象并命名为窗口1,开启线程
    6. new Thread(tw, "窗口2").start(); // 创建线程对象并命名为窗口2,开启线程
    7. }
    8. }
    9. class TicketWindow1 implements Runnable {
    10. private int tickets = 5;
    11. public void run() {
    12. while (tickets > 0) {
    13. Thread th = Thread.currentThread(); // 获取当前线程
    14. String th_name = th.getName(); // 获取当前线程的名字
    15. System.out.println(th_name + " 正在发售第 " + tickets-- + " 张票 ");
    16. }
    17. }
    18. }

    这里举反例进行说明

    让Thread不重复卖票,让Runnable重复卖票。

    ThreadDemo2

    1. package org.example.bk.multithread;
    2. public class ThreadDemo2 {
    3. public static void main(String[] args) {
    4. TicketWindow tw = new TicketWindow();
    5. Thread t1 = new Thread(tw,"窗口1");
    6. Thread t2 = new Thread(tw,"窗口2");
    7. t1.start();
    8. t2.start();
    9. }
    10. }
    11. class TicketWindow extends Thread {
    12. private int tickets = 5;
    13. public void run() {
    14. while (tickets > 0) {
    15. Thread th = Thread.currentThread(); // 获取当前线程
    16. String th_name = th.getName(); // 获取当前线程的名字
    17. System.out.println(th_name + " 正在发售第 " + tickets-- + " 张票 ");
    18. }
    19. }
    20. }

    运行结果

     从结果看到Thread方式可以"不重复卖票"。因为只new TicketWindow对象1次。

    RunnableDemo2

    1. package org.example.bk.multithread;
    2. public class RunnableDemo2 {
    3. public static void main(String[] args) {
    4. TicketWindow1 tw1 = new TicketWindow1();
    5. TicketWindow1 tw2 = new TicketWindow1();
    6. Thread t1 = new Thread(tw1,"窗口1");
    7. Thread t2 = new Thread(tw2,"窗口2");
    8. t1.start();
    9. t2.start();
    10. }
    11. }
    12. class TicketWindow1 implements Runnable {
    13. private int tickets = 5;
    14. public void run() {
    15. while (tickets > 0) {
    16. Thread th = Thread.currentThread(); // 获取当前线程
    17. String th_name = th.getName(); // 获取当前线程的名字
    18. System.out.println(th_name + " 正在发售第 " + tickets-- + " 张票 ");
    19. }
    20. }
    21. }

    运行结果

     从运行结果看,Runnable也可以实现"重复卖票",因为new TicketWindow1对象2次。

    综上所述

    "重不重复卖票"现象并不是Thread和Runnable的不同造成的,而是由于创建线程的方式不同造成的。

    完成!enjoy it!

  • 相关阅读:
    Docker
    Spring Bean如何定义继承呢?
    ESIM实战文本匹配
    在Unity快速搭建一个接收http请求
    session会话机制
    _001_Zotero入门
    Vue 中 (moment)操作日期的加减与展示
    进销存仓库管理系统的优势(一)
    说一下 TCP/IP 协议?以及每层的作用?
    but it set boost_system_FOUND to FALSE so package “boost_system“ is
  • 原文地址:https://blog.csdn.net/qq_42881421/article/details/127424007