多线程Thread类和Runnable接口资源共享问题分析

标签: 多线程Thread类和Runnable接口资源共享问题
23人阅读 评论(0) 收藏 举报
分类:

最近在看多线程时,一直迷茫为什么继承Thread类的多线程不能实现资源共享,但是实现Runnable接口的多线程却能实现资源共享,先看一段经典的卖票多线程,将程序修改一下,使运行结果直观。 
首先是实现Runnable接口来实现多线程:

public class TicketSaleMain implements Runnable{
    private int num = 5;            //票数为5

    public void run() {
        System.out.println(Thread.currentThread().getName() );//线程运行时先打印线程名称
        for(int i=0; i<10; i++){
            if(this.num>0){            //打印卖票信息
                 System.out.println(Thread.currentThread().getName() + "卖票: " + this.num--);
            }
        }
    }
    public static void main(String[] args) {
        TicketSaleMain ticketThread = new TicketSaleMain();       //声明了一个实现Runnable接口的对象A
        Thread th1 = new Thread(ticketThread);    //线程一使用A
        th1.setName("窗口一");
        Thread th2 = new Thread(ticketThread);    //线程二使用A
        th2.setName("窗口二");
        Thread th3 = new Thread(ticketThread);    //线程三使用A
        th3.setName("窗口三");
        th1.start();
        th2.start();
        th3.start();
    }
}

运行结果:

窗口二
窗口一
窗口二卖票: 5
窗口一卖票: 4
窗口二卖票: 3
窗口一卖票: 2
窗口二卖票: 1
窗口三

因为3个线程共享了TicketSaleMain声明的放在堆里的对象ticketThread,都指向了这个对象,自然共享了成员属性num=5,相当于把一个任务塞到3个线程中,达到了一个卖票任务可以被3个线程共同执行,所以能达到资源共享,但是上述结果并不是一直一样顺序递减,还会有这样的结果1, 
运行结果1:

窗口一
窗口二
窗口一卖票: 5
窗口一卖票: 3
窗口一卖票: 2
窗口一卖票: 1
窗口二卖票: 4
窗口三

这是因为并发就是这样,如果你没有做并发控制,就会有各种灵异现象,因为线程执行是交错执行的。 
也即,窗口一和窗口二分别先后执行一次this.num–后,窗口一执行了println,但是窗口二过了一阵子才执行了println。 
还会有这样的结果2: 
运行结果2:

窗口一
窗口二
窗口二卖票: 5
窗口一卖票: 5
窗口三
窗口二卖票: 4
窗口三卖票: 2
窗口一卖票: 3
窗口二卖票: 1

窗口一和窗口二分别同时执行一次this.num–后,一起执行println造成,这种情况在执行了很多次才会出现一次,如果想要更明显,可以修改run方法如下:

    public void run() {
        System.out.println(Thread.currentThread().getName() );//线程运行时先打印线程名称
        for(int i=0; i<10; i++){
             try {                
                 Thread.sleep(1000);    //让线程执行的时候休眠1000ms            
                 } 
            catch (InterruptedException e) {                
                e.printStackTrace();            
                }     
            if(this.num>0){            //打印卖票信息
                 System.out.println(Thread.currentThread().getName() + "卖票: " + this.num--);
            }
        }
    }

运行结果:

窗口二
窗口一
窗口三
窗口二卖票: 5
窗口三卖票: 4
窗口一卖票: 3
窗口二卖票: 2
窗口三卖票: 1
窗口一卖票: 1

上述这些情况都是因为没有对多线程进行并发控制,需要对线程进行同步操作,避免线程对资源竞争,防止出现线程安全问题,同步操作使得在同一个时间段内只有一个线程能进行System.out.println(Thread.currentThread().getName() + "卖票: " + this.num--);其余线程必须等待此线程完成后才能继续执行。

在此我们用synchronized关键字来同步(如下两种方式任选其一):

①可以同步方法

同步方法:就是可以把for循环内的代码封装成一个方法booking(),并用public synchronized void声明,然后再run()中调用booking方法

②也可以同步代码块

同步代码块:可以用synchronized (this) { }将for循环后的代码块包起来,以此来进行同步控制。

******************************************************************************

然后是继承Thread类来实现多线程:

在此就不多说了,直接上代码:

public class TicketSaleMain extends Thread
    private int num = 5;        //总共票数设定为5张
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() ); //线程运行时先打印线程名称
        for(int i=0; i<10; i++){
            if(this.num>0){        //打印卖票信息
                 System.out.println(Thread.currentThread().getName() + "卖票: " + this.num--);
            }
        }
}
public static void main(String[] args) {
    TicketSaleMain th1 = new TicketSaleMain();        //声明的对象线程一
    th1.setName("窗口一");
    TicketSaleMain th2 = new TicketSaleMain();        //声明的对象线程二
    th2.setName("窗口二");
    TicketSaleMain th3 = new TicketSaleMain();        //声明的对象线程三
    th3.setName("窗口三");                //分别启动三个线程
    th1.start();
    th2.start();
    th3.start();
    }
}

运行结果:

窗口二
窗口一
窗口二卖票: 5
窗口二卖票: 4
窗口二卖票: 3
窗口二卖票: 2
窗口二卖票: 1
窗口一卖票: 5
窗口三
窗口一卖票: 4
窗口三卖票: 5
窗口一卖票: 3
窗口三卖票: 4
窗口一卖票: 2
窗口三卖票: 3
窗口一卖票: 1
窗口三卖票: 2
窗口三卖票: 1

从运行结果看,继承Thread类实现多线程,几个线程就要声明几个对象,每个对象都绑定了num=5,相当于每个卖票任务都绑定了一个线程,每个线程执行的卖票任务不是同一个,因此没有实现资源共享。

转载自: https://blog.csdn.net/lyt_7cs1dn9/article/details/52486317
查看评论

Runnable可以实现资源共享但Thread不能实现资源共享的原因

转自:http://blog.csdn.net/javaniceyou/article/details/6859305 线程的两种实现方式,通过实现Runnable接口的线程方式可以实现资源的共享,...
  • edmond999
  • edmond999
  • 2015-04-16 12:02:04
  • 7771

多线程——继承Thread 类和实现Runnable 接口的区别

java中我们想要实现多线程常用的有两种方法,继承Thread 类和实现Runnable 接口,有经验的程序员都会选择实现Runnable接口 ,其主要原因有以下两点:  首先,java只能单继承,因...
  • u010926964
  • u010926964
  • 2017-07-11 14:24:39
  • 465

Runnable可以实现资源共享但Thread不能实现资源共享?不,Thread能!

很多人说Thread不能共享资源,其实只是不适合共享资源,而不是不能
  • u013638600
  • u013638600
  • 2015-04-28 20:47:48
  • 949

多线程——Java中继承Thread类与实现Runnable接口的区别

线程我只写过继承Thread类的,后来知道java多线程有三种方式,今天首先比较一下常用的继承Thread类和实现Runnable接口的区别。               按着Ctrl键进入Threa...
  • xdd19910505
  • xdd19910505
  • 2016-02-24 18:27:13
  • 7950

java--(多线程创建的两种方式Thread类和Runnable接口)

(一)继承Thread类创建多线程----单线程 下面的代码是一个死循环,但是不会执行main里面的循环语句,而是run()里面的语句,这是因为该程序是一个单线程程序,当调用MyThread类的ru...
  • qq_32823673
  • qq_32823673
  • 2017-11-28 17:24:02
  • 2409

实现Runnable接口的多线程可以实现属性共享的原因

为什么实现Runnable接口来创建的线程可以实现数据共享? 先来看看我们是实现Runnable接口时是如何启动线程的: class R implements Runnable{}        ...
  • zuolovefu
  • zuolovefu
  • 2014-10-19 18:13:16
  • 2580

多线程资源共享集合

  • 2014年04月13日 11:34
  • 36KB
  • 下载

何时继承Thread类何时选择实现Runnable接口

差别 1.继承thread的类,在创建它的对象时,创建两个thread,他们各自执行各自的进程,互不干扰,Thread类当中的数据也是彼此独立。 但是在Runnable中,创建一个实现了Runna...
  • D_inosaur
  • D_inosaur
  • 2017-03-17 17:12:49
  • 668

如果一个类通过继承Thread来实现多线程的话,则不适合多个线程共享资源,而通过实现Runnable就可以做到这一点

如果一个类通过继承Thread来实现多线程的话,则不适合多个线程共享资源,而通过实现Runnable就可以做到这一点,下面给lz举个例子: Java code class MyTheard ex...
  • caoyizhi1987
  • caoyizhi1987
  • 2011-11-16 15:01:26
  • 2280

实现多线程的两种方法:继承Thread类或实现Runnable接口

实现多线程的两种方法:继承Thread类或实现Runnable接口Java中实现多线程有两种方法:继承Thread类和实现Runnable接口,在程序开发中只要是多线程,我们一般都是实现Runnabl...
  • u010412719
  • u010412719
  • 2015-07-22 21:27:02
  • 1775
    个人资料
    持之以恒
    等级:
    访问量: 4万+
    积分: 1087
    排名: 4万+