欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【稀里糊涂多线程】实现Runnable后多线程操作同一个实例变量注意点

程序员文章站 2024-01-26 08:19:22
...

在讲述继承Thread和实现Runnable接口时,我们说到两者的区别中有一个区别是实现Runnable接口,可以多个线程操作同一个实例变量,如下代码,抢票的例子,3个线程共同去操作同一个变量ticket:

public class MainClass {
  public static void main(String [] args) throws InterruptedException {
    Runnable runnable = new QiangPiaoThread();
    new Thread(runnable,"小明").start();
    new Thread(runnable,"小红").start();
    new Thread(runnable,"小牛").start();

  }
}

class QiangPiaoThread implements  Runnable{
  Integer ticket = 10;
  @Override
  public void run() {
    String name = Thread.currentThread().getName();
    while(ticket>0) {
      try {
        synchronized (ticket) {
        System.out.println(name + "抢票了第" +ticket + "张票");
        ticket = ticket -1;
       }

        Thread.sleep(1000L);
        }catch(Exception ex){}
      }
    }
}

// 运行结果
小明抢票了第10张票
小红抢票了第9张票
小牛抢票了第8张票
小牛抢票了第7张票
小明抢票了第6张票
小红抢票了第5张票
小红抢票了第4张票
小明抢票了第3张票
小牛抢票了第2张票
小红抢票了第1张票
小明抢票了第0张票
小牛抢票了第-1张票

这面这段代码通过Runnable来让三个线程操作同一个ticket变量,这事实现接口的一个特点。但是我这里要讲的是,当synchronized和ticket为了实现原子性共同使用时,要注意的一些问题,

从代码的打印结果可以看出,虽然我使用了synchronized并且也用ticket作为了锁,但是结果还是错的。错误的原因是while条件中的ticket>0导致的。因为while(ticket>0)这里还没有通过synchronized实现原子性的操作,所以导致当ticket=1时,小红,小明,小牛都进入了while中,这样就出现三个线程都会执行ticket-1的操作。导致出现了0和-1的错误数据。

 

所以在实现Runnable来操作同一个变量如ticket时,已经要将对ticket的操作(比较,加减等操作),放到synchronized,下面对其进行修改,如下:

public class MainClass {
  public static void main(String [] args) throws InterruptedException {
    Runnable runnable = new QiangPiaoThread();
    new Thread(runnable,"小明").start();
    new Thread(runnable,"小红").start();
    new Thread(runnable,"小牛").start();

  }
}

class QiangPiaoThread implements  Runnable{
  Integer ticket = 10;
  @Override
  public void run() {
    String name = Thread.currentThread().getName();
    while(true) {
      try {
       synchronized (ticket) {
         if(ticket == 0) { //将对ticket的操作放到这里
           break;
         }
         System.out.println(name + "抢票了第" +ticket + "张票");
         ticket = ticket -1;
       }

        Thread.sleep(1000L);
        }catch(Exception ex){}
      }
    }
}

// 运行结果
小明抢票了第10张票
小红抢票了第9张票
小牛抢票了第8张票
小明抢票了第7张票
小红抢票了第6张票
小牛抢票了第5张票
小红抢票了第4张票
小明抢票了第3张票
小牛抢票了第2张票
小红抢票了第1张票

Process finished with exit code 0

这样通过将ticket放到synchronized中,作为原子操作的一部分,这样就不会出现错结果了。所以当使用Runnable来让多个线程使用同一个共享变量时,一定要注意对这个共享变量如代码中ticket的操作的的位置,一定要在synchronized中。