java多线程卖电影票的三种实现方式

简介: java多线程卖电影票的三种实现方式

一、需求描述

某电影院目前正在上映国产大片,共有1000张票,而它有2个窗口卖票,请设计一个程序模拟该电影院卖票

二、实现方式

1、继承Thread类的方式

自定义开发一个MyThread类,来继承Thread类,重写run方法,定义一个ticket共享变量,表示当前卖的是第几张票,一定要使用static关键字来修饰,这样可以确保每一个线程对象都共享这一个变量。具体代码如下:

MyThread类

package com.hidata.hiops.paas.demo;
/**
 * @Description :
 * @Date: 2023-10-08 10:38
 */
public class MyThread extends Thread {
    public static int ticket = 0;
    @Override
    public void run() {
        while (true){
            synchronized (MyThread.class){
                if (ticket < 1000){
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket ++;
                    System.out.println(getName() + "正在卖第" + ticket + "张票, 剩余 " + (1000 - ticket) + "张");
                }else{
                    break;
                }
            }
        }
    }
}

测试类

package com.hidata.hiops.paas.demo;
/**
 * @Description :
 * @Date: 2023-10-08 10:46
 */
public class TestDemo {
    public static void main(String[] args) {
        /**
         *方式一:继承Thread类
         */
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t1.start();
        t2.start();
    }
}

运行结果

......................
窗口1正在卖第978张票, 剩余 22张
窗口1正在卖第979张票, 剩余 21张
窗口1正在卖第980张票, 剩余 20张
窗口1正在卖第981张票, 剩余 19张
窗口1正在卖第982张票, 剩余 18张
窗口1正在卖第983张票, 剩余 17张
窗口2正在卖第984张票, 剩余 16张
窗口2正在卖第985张票, 剩余 15张
窗口2正在卖第986张票, 剩余 14张
窗口2正在卖第987张票, 剩余 13张
窗口2正在卖第988张票, 剩余 12张
窗口2正在卖第989张票, 剩余 11张
窗口2正在卖第990张票, 剩余 10张
窗口2正在卖第991张票, 剩余 9张
窗口2正在卖第992张票, 剩余 8张
窗口2正在卖第993张票, 剩余 7张
窗口2正在卖第994张票, 剩余 6张
窗口2正在卖第995张票, 剩余 5张
窗口2正在卖第996张票, 剩余 4张
窗口2正在卖第997张票, 剩余 3张
窗口2正在卖第998张票, 剩余 2张
窗口2正在卖第999张票, 剩余 1张
窗口2正在卖第1000张票, 剩余 0张

2、实现Runnable接口的方式

自定义开发一个MyRun类,来实现Runnable接口,重写run方法,定义一个ticket变量,表示当前卖的是第几张票,此时ticket变量,可以不用static关键字来修饰,因为只会创建一个MyRun实例,所以不存在变量不一致的问题,具体代码如下:

MyRun类

package com.hidata.hiops.paas.demo;
/**
 * @Description :
 * @Date: 2023-10-08 10:50
 */
public class MyRun implements Runnable {
    int ticket = 0;
    @Override
    public void run() {
        while (true){
            synchronized (MyRun.class){
                if (ticket < 1000){
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket ++;
                    System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票, 剩余 " + (1000 - ticket) + "张");
                }else{
                    break;
                }
            }
        }
    }
}

测试类

package com.hidata.hiops.paas.demo;
/**
 * @Description :
 * @Date: 2023-10-08 10:46
 */
public class TestDemo {
    public static void main(String[] args) {
       /**
         * 方式二:实现Runnable接口
         */
        MyRun myRun = new MyRun();
        Thread tt1 = new Thread(myRun);
        Thread tt2 = new Thread(myRun);
        tt1.setName("窗口1");
        tt2.setName("窗口2");
        tt1.start();
        tt2.start();
    }
}

运行结果

3、使用Lock锁的方式

自定义开发一个MyLock类,来实现Runnable接口,重写run方法,定义一个ticket变量,表示当前卖的是第几张票,此时ticket变量,可以不用static关键字来修饰,再创建一个Lock锁对象,也不需要使用static修饰,所有的线程对象共用一把锁。因为只会创建一个MyRun实例,所以不存在变量不一致的问题,具体代码如下:

MyLock类

package com.hidata.hiops.paas.demo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @Description :
 * @Date: 2023-10-08 10:50
 */
public class MyLock implements Runnable {
    int ticket = 0;
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            lock.lock();
            try {
                if (ticket == 1000){
                    break;
                }else{
                    Thread.sleep(30);
                    ticket ++;
                    System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票, 剩余 " + (1000 - ticket) + "张");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

测试类

package com.hidata.hiops.paas.demo;
/**
 * @Description :
 * @Date: 2023-10-08 10:46
 */
public class TestDemo {
    public static void main(String[] args) {
        /**
         * 方式三:使用Lock锁
         */
        MyLock myLock = new MyLock();
        Thread r1 = new Thread(myLock);
        Thread r2 = new Thread(myLock);
        r1.setName("窗口1");
        r2.setName("窗口2");
        r1.start();
        r2.start();
    }
}

运行结果


相关文章
|
3天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
36 14
|
6天前
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
34 13
|
6天前
|
安全 Java 开发者
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。
|
1月前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
109 17
|
2月前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
1月前
|
缓存 安全 算法
Java 多线程 面试题
Java 多线程 相关基础面试题
|
2月前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
2月前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
2月前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
75 3
|
2月前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
224 2