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

背景

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-- + " 张票 ");
        }
    }
}

运行结果

Java多线程之Thread和Runnable关于共享资源的对比_第1张图片

 从结果中看到每张票都都卖了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-- + " 张票 ");
        }
    }
}

运行结果

Java多线程之Thread和Runnable关于共享资源的对比_第2张图片

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

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

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

Java多线程之Thread和Runnable关于共享资源的对比_第3张图片

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-- + " 张票 ");
        }
    }
}

运行结果

Java多线程之Thread和Runnable关于共享资源的对比_第4张图片

 从结果看到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-- + " 张票 ");
        }
    }
}

运行结果

Java多线程之Thread和Runnable关于共享资源的对比_第5张图片

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

综上所述

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

完成!enjoy it!

你可能感兴趣的:(多线程,java,多线程)