java多线程之火车售票系统模拟实例

1.前言

为了学习多线程共享与通信,我们模拟一个火车售票系统,假设有10张火车票,三个窗口(也就是三个线程)同时进行售票。

2.非同步代码

package com.tl.skyLine.thread; 

/**
 * Created by tl on 17/3/6.
 */
public class SellTicket { 

  public static void main(String[] args) {
    TicketWindow tw = new TicketWindow();
    Thread t1 = new Thread(tw, "一号窗口");
    Thread t2 = new Thread(tw, "二号窗口");
    Thread t3 = new Thread(tw, "三号窗口");
    t1.start();
    t2.start();
    t3.start();
  }
} 

class TicketWindow implements Runnable {
  private int tickets = 10; 

  @Override
  public void run() {
    while (true) {
      if (tickets > 0) {
        System.out.println("还剩余票:" + tickets + "张");
        tickets--;
        System.out.println(Thread.currentThread().getName() + "卖出一张火车票,还剩" + tickets + "张");
      } else {
        System.out.println("余票不足,暂停出售!");
//        wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
        try {
          Thread.sleep(1000 * 60 * 5);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }
} 

打印结果:

还剩余票:10张
还剩余票:10张
还剩余票:10张
二号窗口卖出一张火车票,还剩7张
还剩余票:7张
三号窗口卖出一张火车票,还剩8张
一号窗口卖出一张火车票,还剩9张
还剩余票:6张
还剩余票:6张
二号窗口卖出一张火车票,还剩6张
还剩余票:4张
三号窗口卖出一张火车票,还剩4张
还剩余票:3张
一号窗口卖出一张火车票,还剩5张
三号窗口卖出一张火车票,还剩2张
还剩余票:2张
三号窗口卖出一张火车票,还剩1张
还剩余票:1张
三号窗口卖出一张火车票,还剩0张
余票不足,暂停出售!
二号窗口卖出一张火车票,还剩3张
余票不足,暂停出售!
还剩余票:2张
一号窗口卖出一张火车票,还剩-1张
余票不足,暂停出售! 

上面结果,可以清楚地看到,由于三个线程可以同时访问一个任务,也就是售票任务,会出现火车票还剩-1张这种不合实际的问题,之所以出现是因为假设在某一瞬间,tickets为1时,tickets > 0为true,A线程运行到tickets--这一行代码,此时还没有减去1,同时另外一个线程B刚好运行到tickets > 0这一行代码,判断成功,开始执行卖票,此时A线程减去一张票,tickets=0,然后B线程又减去一张,则剩-1张。所以此时需要用到同步锁synchronized。保证某一时刻只能有一个线程执行售票功能。

3.同步代码

package com.tl.skyLine.thread; 

/**
 * Created by tl on 17/3/6.
 */
public class SellTicket { 

  public static void main(String[] args) {
    TicketWindow tw = new TicketWindow();
    Thread t1 = new Thread(tw, "一号窗口");
    Thread t2 = new Thread(tw, "二号窗口");
    Thread t3 = new Thread(tw, "三号窗口");
    t1.start();
    t2.start();
    t3.start();
  }
} 

class TicketWindow implements Runnable {
  private int tickets = 10; 

  @Override
  public synchronized void run() {
    while (true) {
      if (tickets > 0) {
        System.out.println(Thread.currentThread().getName() + "准备出票,还剩余票:" + tickets + "张");
        tickets--;
        System.out.println(Thread.currentThread().getName() + "卖出一张火车票,还剩" + tickets + "张");
      } else {
        System.out.println("余票不足,暂停出售!");
//        wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
        try {
          Thread.sleep(1000 * 60 * 5);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }
} 

等同于:

class TicketWindow implements Runnable {
  private int tickets = 10;                                                 

  @Override
  public void run() {
    while (true) {
      synchronized (this) {
        if (tickets > 0) {
          System.out.println(Thread.currentThread().getName() + "准备出票,还剩余票:" + tickets + "张");
          tickets--;
          System.out.println(Thread.currentThread().getName() + "卖出一张火车票,还剩" + tickets + "张");
        } else {
          System.out.println("余票不足,暂停出售!");
//        wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
          try {
            Thread.sleep(1000 * 60 * 5);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }
  }
}              

结果:

一号窗口准备出票,还剩余票:10张
一号窗口卖出一张火车票,还剩9张
一号窗口准备出票,还剩余票:9张
一号窗口卖出一张火车票,还剩8张
一号窗口准备出票,还剩余票:8张
一号窗口卖出一张火车票,还剩7张
一号窗口准备出票,还剩余票:7张
一号窗口卖出一张火车票,还剩6张
一号窗口准备出票,还剩余票:6张
一号窗口卖出一张火车票,还剩5张
一号窗口准备出票,还剩余票:5张
一号窗口卖出一张火车票,还剩4张
一号窗口准备出票,还剩余票:4张
一号窗口卖出一张火车票,还剩3张
一号窗口准备出票,还剩余票:3张
一号窗口卖出一张火车票,还剩2张
一号窗口准备出票,还剩余票:2张
一号窗口卖出一张火车票,还剩1张
一号窗口准备出票,还剩余票:1张
一号窗口卖出一张火车票,还剩0张
余票不足,暂停出售! 

synchronized:

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

以上这篇java多线程之火车售票系统模拟实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • java多线程学习笔记之自定义线程池

    当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的. public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueu

  • java多线程编程学习(线程间通信)

    一.概要 线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通信就是成为整体的必用方案之一.可以说,使线程进行通信后,系统之间的交互性会更强大,在大大提高cpu利用率的同时还会使程序员对各线程任务在处理过程中进行有效的把控和监督. 二.等待/通知机制 1."wait/notify"机制:等待/通知机制,wait使线程暂停运行,而notify 使暂停的线程继续运行.用一个厨师和服务员的交互来说明: (1) 服务员取到菜的时间取决于厨师,所以服务员就有&

  • Java多线程用法的实例详解

    Java多线程用法的实例详解 前言: 最全面的java多线程用法解析,如果你对Java的多线程机制并没有深入的研究,那么本文可以帮助你更透彻地理解Java多线程的原理以及使用方法. 1.创建线程 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例.Thread构造函数: public Thread( ); p

  • Java基于Socket实现简单的多线程回显服务器功能示例

    本文实例讲述了Java基于Socket实现简单的多线程回显服务器功能.分享给大家供大家参考,具体如下: 需要两个类,一个是EchoServer,代表服务器.另外一个是EchoServerClient,代表客户端.代码如下: package interview; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter

  • java 多线程的三种构建方法

    java  多线程的三种构建方法 继承Thread类创建线程类 public class Thread extends Object implements Runnable 定义Thread类的子类,并重写其run()方法 创建Thread子类的实例,即创建了线程对象 调用线程对象的start()方法启动线程 public class FirstThread extends Thread { public void run(){ for(int i=0;i<100;i++){ /* * Thre

  • java 中多线程生产者消费者问题详细介绍

    java 中多线程生产者消费者问题 前言: 一般面试喜欢问些线程的问题,较基础的问题无非就是死锁,生产者消费者问题,线程同步等等,在前面的文章有写过死锁,这里就说下多生产多消费的问题了 import java.util.concurrent.locks.*; class BoundedBuffer { final Lock lock = new ReentrantLock();//对象锁 final Condition notFull = lock.newCondition(); //生产者监视

  • java多线程学习之死锁的模拟和避免(实例讲解)

    1.死锁 死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放.由于线程被无限期地阻塞,因此程序不可能正常终止. Java 死锁产生的四个必要条件: 1.互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用 2.不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放. 3.请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有. 4.循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的

  • Java多线程的用法详细介绍

    Java多线程的用法详细介绍 最全面的Java多线程用法解析,如果你对Java的多线程机制并没有深入的研究,那么本文可以帮助你更透彻地理解Java多线程的原理以及使用方法. 1.创建线程 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例.Thread构造函数: public Thread( ); publi

  • Java Socket实现多线程通信功能示例

    本文实例讲述了Java Socket实现多线程通信功能的方法.分享给大家供大家参考,具体如下: 前面的文章<Java Socket实现单线程通信的方法示例>说到怎样写一个最简单的Java Socket通信,但是文章中的例子有一个问题就是Server只能接受一个Client请求,当第一个Client连接后就占据了这个位置,后续Client不能再继续连接,所以需要做些改动,当Server没接受到一个Client连接请求之后,都把处理流程放到一个独立的线程里去运行,然后等待下一个Client连接请求

  • java 多线程Thread与runnable的区别

    java 多线程Thread与runnable的区别 java中实现多线程的方法有两种:继承Thread类和实现runnable接口 1,继承Thread类,重写父类run()方法 public class thread1 extends Thread { public void run() { for (int i = 0; i < 10000; i++) { System.out.println("我是线程"+this.getId()); } } public static

随机推荐