精华内容
下载资源
问答
  • 2020-12-23 05:32:31

    说明:

    利用多线程对网络抢票进行模拟演示。

    技术点:

    (1)实现Runnable接口

    (2)重写run方法

    (3)创建Thread对象调用start()方法

    (4)线程锁

    (5)线程休眠

    package kgc.xiancheng.xi3;

    //多线程抢票

    public class TestThread2 implements Runnable {

    private int count=10; //库存总票数

    private int num=0; //抢票次数

    @Override

    public void run() {

    int i=0;

    while (true){

    synchronized (this){ //线程锁

    if(count==0){ //抢票完后退出

    return;

    }

    if(Thread.currentThread().getName().equals("黄牛党")){ //对线程进行干预

    if(i==1){ //只允许抢一次票

    System.out.println();

    System.out.println("你是黄牛!,不准你抢票了");

    break;

    }

    i++;

    }

    try {

    Thread.sleep(100);//模拟网络耗时

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    count--; //计数器

    num++;

    System.out.println();

    System.out.println(Thread.currentThread().getName()+"抢到第"+num+"张票了,还有"+count+"张票");

    }

    }

    }

    public static void main(String[] args) {

    TestThread2 t = new TestThread2(); //创建线程对象

    Thread t1 = new Thread(t,"曹操"); //利用兄弟类启动线程体

    Thread t2 = new Thread(t,"孙权");

    Thread t3 = new Thread(t,"黄牛党");

    t1.start(); //启动线程1

    t2.start();

    t3.start();

    }

    }

    运行效果:

    image.png

    更多相关内容
  • 线程同时共享,同一个全局变量或者静态变量,做写得操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。 举个例子,同时售卖火车票 package ...
    1. 理解线程安全?
    2. synchronized用法
    3. 死锁
    4. Java内存模型
    5. Vlolatile
    6. ThreadLock

    什么是线程安全

    当多个线程同时共享,同一个全局变量或者静态变量,做写得操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。

    举个例子,同时售卖火车票

    package com.evan.springboot.concurrentDemo.ticket;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 10:01
     */
    public class ThreadTran implements Runnable {
        private int count=100;
        private static Object object=new Object();
        @Override
        public void run() {
            while (count>0){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sale();
            }
        }
    
        public void sale(){
            System.out.println(Thread.currentThread().getName()+",正在出售第"+(100-count+1));
            count--;
        }
    }
    
    
    package com.evan.springboot.concurrentDemo.ticket;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 10:02
     */
    public class Demo {
        public static void main(String[] args) {
            ThreadTran threadTran = new ThreadTran();
            Thread t1 = new Thread(threadTran, "一号窗口");
            Thread t2 = new Thread(threadTran, "二二");
            t1.start();
            t2.start();
        }
    }
    
    

    在这里插入图片描述
    一号窗口和二号窗口同时出售火车第一张和第七张,部分火车票会重复出售。
    结论发现,多个线程共享同一个全局成员变量时,做写的操作可能会发生数据冲突问题。

    线程安全问题的解决办法:

    问:如何解决多线程之间线程安全问题?
    答:使用多线程之间同步synchronized或使用锁(lock)。
    问:为什么使用线程同步或使用锁能解决线程安全问题呢?
    答:将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。
    问:什么是多线程之间同步?
    答:当多个线程共享同一个资源,不会受到其他线程的干扰。

    package com.evan.springboot.concurrentDemo.ticket;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 10:01
     */
    public class ThreadTran implements Runnable {
        private int count=100;
        private static Object object=new Object();
        @Override
        public void run() {
            while (count>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sale();
            }
        }
    
        public void sale(){
            if (count>0) {
                synchronized (object) {
                    if (count > 0) {
                        System.out.println(Thread.currentThread().getName() + ",正在出售第" + (100 - count + 1));
                        count--;
                    }
                }
            }
        }
    }
    
    

    多线程死锁

    • 死锁的定义:

    多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

    • 死锁产生的原因
    1. 系统资源的竞争
      通常系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,使得进程在 运行过程中,会因争夺资源而陷入僵局,如磁带机、打印机等。只有对不可剥夺资源的竞争 才可能产生死锁,对可剥夺资源的竞争是不会引起死锁的。

    2. 进程推进顺序非法
      进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。例如,并发进程 P1、P2分别保持了资源R1、R2,而进程P1申请资源R2,进程P2申请资源R1时,两者都 会因为所需资源被占用而阻塞。

    信号量使用不当也会造成死锁。进程间彼此相互等待对方发来的消息,结果也会使得这 些进程间无法继续向前推进。例如,进程A等待进程B发的消息,进程B又在等待进程A 发的消息,可以看出进程A和B不是因为竞争同一资源,而是在等待对方的资源导致死锁。

    1. 死锁产生的必要条件
      产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生。

    互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
    不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
    请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
    循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, …, pn},其中Pi等 待的资源被P(i+1)占有(i=0, 1, …, n-1),Pn等待的资源被P0占有,如图2-15所示。

    锁顺序死锁

    package com.evan.springboot.concurrentDemo.DeadLock;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 10:19
     */
    public class LeftRightDeadLock {
        private final static Object left=new Object();
        private final Object right=new Object();
        public boolean flag=true;
    
    
        public void leftRight() throws InterruptedException {
            synchronized (left){
                Thread.sleep(200);
                synchronized (right){
                    Thread.sleep(60);
                }
            }
        }
    
        public void rightLeft() throws InterruptedException {
            synchronized (right){
                Thread.sleep(200);
                synchronized (left){
                    Thread.sleep(60);
                }
            }
        }
    
    }
    
    
    package com.evan.springboot.concurrentDemo.DeadLock;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 10:21
     */
    public class Demo {
        public static void main(String[] args) {
            LeftRightDeadLock leftRightDeadLock = new LeftRightDeadLock();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        leftRightDeadLock.leftRight();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        leftRightDeadLock.rightLeft();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
    
    
    package com.evan.springboot.concurrentDemo.DeadLock;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 10:27
     */
    public class DeadLockDemo implements Runnable {
        public int flag = 1;
        //静态对象是类的所有对象共享的
        private static Object o1 = new Object(), o2 = new Object();
    
        @Override
        public void run() {
            System.out.println("flag=" + flag);
            if (flag == 1) {
                synchronized (o1) {
                    try {
                        Thread.sleep(500);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    synchronized (o2) {
                        System.out.println("1");
                    }
                }
            }
            if (flag == 0) {
                synchronized (o2) {
                    try {
                        Thread.sleep(500);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    synchronized (o1) {
                        System.out.println("0");
                    }
                }
            }
        }
    
        public static void main(String[] args) {
    
            DeadLockDemo td1 = new DeadLockDemo();
            DeadLockDemo td2 = new DeadLockDemo();
            td1.flag = 1;
            td2.flag = 0;
            //td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。
            //td2的run()可能在td1的run()之前运行
            new Thread(td1).start();
            new Thread(td2).start();
    
        }
    }
    
    

    如何避免死锁

    在有些情况下死锁是可以避免的。三种用于避免死锁的技术:

    加锁顺序(线程按照一定的顺序加锁)
    加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
    死锁检测

    多线程的三大特性

    原子性
    可见性
    有序性

    • 原子性

    即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
    一个很经典的例子就是银行账户转账问题:
    比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。这2个操作必须要具备原子性才能保证不出现一些意外的问题。
    我们操作数据也是如此,比如i = i+1;其中就包括,读取i的值,计算i,写入i。这行代码在Java中是不具备原子性的,则多线程运行肯定会出问题,所以也需要我们使用同步和lock这些东西来确保这个特性了。
    原子性其实就是保证数据一致、线程安全一部分,

    • 可见性
      当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
      若两个线程在不同的cpu,那么线程1改变了i的值还没刷新到主存,线程2又使用了i,那么这个i值肯定还是之前的,线程1对变量的修改线程没看到这就是可见性问题。
    • 有序性

    程序执行的顺序按照代码的先后顺序执行。
    一般来说处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。如下:
    int a = 10; //语句1
    int r = 2; //语句2
    a = a + 3; //语句3
    r = a*a; //语句4
    则因为重排序,他还可能执行顺序为 2-1-3-4,1-3-2-4
    但绝不可能 2-1-4-3,因为这打破了依赖关系。
    显然重排序对单线程运行是不会有任何问题,而多线程就不一定了,所以我们在多线程编程时就得考虑这个问题了。

    java内存模型

    共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
    在这里插入图片描述
    从上图来看,线程A与线程B之间如要通信的话,必须要经历下面2个步骤:

    1. 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。
    2. 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。
      在这里插入图片描述
      如上图所示,本地内存A和B有主内存中共享变量x的副本。假设初始时,这三个内存中的x值都为0。线程A在执行时,把更新后的x值(假设值为1)临时存放在自己的本地内存A中。当线程A和线程B需要通信时,线程A首先会把自己本地内存中修改后的x值刷新到主内存中,此时主内存中的x值变为了1。随后,线程B到主内存中去读取线程A更新后的x值,此时线程B的本地内存的x值也变为了1。
      从整体来看,这两个步骤实质上是线程A在向线程B发送消息,而且这个通信过程必须要经过主内存。JMM通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性保证。

    总结:什么是Java内存模型:java内存模型简称jmm,定义了一个线程对另一个线程可见。共享变量存放在主内存中,每个线程都有自己的本地内存,当多个线程同时访问一个数据的时候,可能本地内存没有及时刷新到主内存,所以就会发生线程安全问题。

    Volatitle

    Volatile 关键字的作用是变量在多个线程之间可见。

    package com.evan.springboot.concurrentDemo;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 11:28
     */
    public class ThreadVolatitleDemo extends Thread {
    
        public boolean flag=true;
        @Override
        public void run(){
            System.out.println("开始执行子线程");
            while (flag){
    
            }
            System.out.println("线程停止");
        }
    
        public void setRunging (boolean flag){
            this.flag=flag;
        }
    
        public static void main(String[] args) throws InterruptedException {
            ThreadVolatitleDemo demo = new ThreadVolatitleDemo();
            demo.start();
            Thread.sleep(3000);
            demo.setRunging(false);
            System.out.println("flag 已经设置成false");
            Thread.sleep(1000);
            System.out.println(demo.flag);
    
        }
    }
    
    

    在这里插入图片描述
    已经将结果设置为fasle为什么?还一直在运行呢。
    原因:线程之间是不可见的,读取的是副本,没有及时读取到主内存结果。
    解决办法使用Volatile关键字将解决线程之间可见性, 强制线程每次读取该值的时候都去“主内存”中取值

    Volatitle非原子性

    package com.evan.springboot.concurrentDemo;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 11:35
     */
    public class ThreadNoAtomic extends Thread {
        private static volatile int count;
    
        private  static void addCount(){
            for (int i = 0; i < 1000; i++) {
                count++;
            }
            System.out.println(count);
        }
    
        @Override
        public void run() {
            addCount();
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new ThreadNoAtomic().start();
            }
        }
    }
    
    

    在这里插入图片描述
    使用AtomicInteger原子类

    package com.evan.springboot.concurrentDemo;
    
    import sun.java2d.pipe.SpanIterator;
    
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 11:40
     */
    public class ThreadAtomicInteger extends Thread {
    
        private static AtomicInteger atomicInteger=new AtomicInteger(0);
    
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                atomicInteger.incrementAndGet();
            }
            System.out.println(atomicInteger.get());
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new ThreadAtomicInteger().start();
            }
        }
    }
    
    

    volatile与synchronized区别
    仅靠volatile不能保证线程的安全性。(原子性)
    ①volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法
    ②volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。
    synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。

    ThreadLocal

    什么是ThreadLocal
    ThreadLocal提高一个线程的局部变量,访问某个线程拥有自己局部变量。
    当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
    ThreadLocal的接口方法
    ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:
    void set(Object value)设置当前线程的线程局部变量的值。
    public Object get()该方法返回当前线程所对应的线程局部变量。
    public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
    protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

    package com.evan.springboot.concurrentDemo.ThreadLocalDemo;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 11:50
     */
    public class Res {
        public static Integer count=0;
    
        public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
            protected Integer initialValue() {
                return 0;
            };
    
        };
    
        public Integer getNum(){
            threadLocal.set(threadLocal.get()+1);
            return  threadLocal.get();
        }
    }
    
    
    package com.evan.springboot.concurrentDemo.ThreadLocalDemo;
    
    /**
     * @author evanYang
     * @version 1.0
     * @date 2020/05/03 12:03
     */
    public class ThreadLocalDemo extends Thread {
        private Res res;
        public ThreadLocalDemo(Res res){
            this.res=res;
        }
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + "---" + "i---" + i + "--num:" + res.getNum());
    
            }
        }
    
        public static void main(String[] args) {
            Res res = new Res();
            ThreadLocalDemo threadLocaDemo1 = new ThreadLocalDemo(res);
            ThreadLocalDemo threadLocaDemo2 = new ThreadLocalDemo(res);
            ThreadLocalDemo threadLocaDemo3 = new ThreadLocalDemo(res);
            threadLocaDemo1.start();
            threadLocaDemo2.start();
            threadLocaDemo3.start();
        }
    }
    
    

    在这里插入图片描述
    ThreadLoca实现原理
    ThreadLoca通过map集合
    Map.put(“当前线程”,值);

    展开全文
  • 多线程 1、创建线程之继承Thread类 1、定义类继承Thread类 2、重写run()方法 3、调用自定义类的start()方法,启动线程 1.1、继承Thread类实现窗口抢票的功能 class SaleTicket extends Thread{ public SaleTicket...

    多线程

    1、创建线程之继承Thread类

    1、定义类继承Thread类
    2、重写run()方法
    3、调用自定义类的start()方法,启动线程

    1.1、继承Thread类实现窗口抢票的功能

    class SaleTicket extends Thread{
        public SaleTicket(String name) {
            super(name);
        }
        //static 说明定义成了公共资源
        static int ticketNum = 10;
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if (ticketNum > 0){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()
                            + ":售出了票,余票有" + (--ticketNum) + "张!");
                }
            }
        }
    }
    
    /**
     * 继承Thread的类中增加构造方法super(name),则可以直接在创建实例时设置线程的名称
     * eg:st4
     */
    public class Demo01 {
        public static void main(String[] args) {
            SaleTicket st1 = new SaleTicket("窗口1");
            //st1.setName("窗口1");
            SaleTicket st2 = new SaleTicket("窗口2");
            SaleTicket st3 = new SaleTicket("窗口3");
            //SaleTicket st4 = new SaleTicket("窗口3");
            st1.start();
            st2.start();
            st3.start();
        }
    }
    

    继承Thread的SaleTicket类中增加构造方法super(name),则可以直接在创建实例时设置线程的名称 ,没有添加构造方法则可以像st1.setName()一样设置,Thread类的其他方法的使用也是一样的

    Thread类常用方法:
    setName():设置线程名称
    yield():礼让线程,将当前cpu执行权让出,但有可能重新抢到cpu执行权
    join():强制执行当前线程,直到该线程死亡,再执行其他线程
    sleep(long ms):线程休眠,当前线程休眠指定毫秒数,休眠结束后,重 新去抢占cpu资源
    setPriority(int num):设置当前线程的优先级

    2、创建线程之实现Runnable接口

    注:在实际开发中,大多选用实现Runnable接口的方法

    Runnable接口只有一个run方法,不能通过调用run方法启动线程,需要借助Thread类来代理,调用Thread类的start方法启动线程。
    静态代理模式,比如房屋中介,婚姻中介等

    2.1、实现Runnable接口实现窗口抢票的功能

    class Station implements Runnable{
        private static int ticket = 10;//10张票
        @Override
        public void run() {
            while (true){
                if (ticket > 0){
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "售出一张票,余票" + (--ticket) + "张...");
                }else {
                    break;
                }
            }
        }
    }
    public class Demo02 {
        public static void main(String[] args) {
            Station s1 = new Station();
            Station s2 = new Station();
            Station s3 = new Station();
            Thread t1 = new Thread(s1,"窗口1");
            Thread t2 = new Thread(s2,"窗口2");
            Thread t3 = new Thread(s3,"窗口3");
            t1.start();
            t2.start();
            t3.start();
        }
    }
    

    3、创建线程之实现Callable接口

    Callable接口只有一个call方法,必须借助FutureTask类,然后通过Thread类来代理,调用Thread类的start方法启动线程

    3.1、实现Callable接口实现窗口抢票的功能

    class Ticket{
        int num = 10;
    }
    class MyThread implements Callable<Boolean>{
        private Ticket ticket;
    
        public MyThread(Ticket ticket) {
    
            this.ticket = ticket;
        }
    
        @Override
        public Boolean call() throws Exception {
            while (true){
                if (ticket.num > 0){
                    Thread.sleep(30);
                    System.out.println(Thread.currentThread().getName() + "售出一张票,余票" + (--ticket.num) + "张...");
                }else {
                    break;
                }
            }
            return null;
        }
    }
    public class Demo03 {
        public static void main(String[] args) {
            Ticket ticket = new Ticket();
            MyThread t1 = new MyThread(ticket);
            MyThread t2 = new MyThread(ticket);
            MyThread t3 = new MyThread(ticket);
            FutureTask ft1 = new FutureTask(t1);
            FutureTask ft2 = new FutureTask(t2);
            FutureTask ft3 = new FutureTask(t3);
            new Thread(ft1,"窗口1").start();
            new Thread(ft2,"窗口2").start();
            new Thread(ft3,"窗口3").start();
        }
    }
    

    Callable接口使用了泛型,可以根据需要定义不同的类型

    创建线程共有以上三种方式,实现Runnable接口为最常用的方式

    遗留问题:线程操作共享资源时肯定会出现问题(重票或负数票),原因就是当一个其中一个线程抢到CPU主动权后,进入休眠状态,票数减一,另一个线程又抢到了相同的票。

    在这里插入图片描述

    展开全文
  • 概要:利用多线程机制模拟售票过程。有俩个线程模拟售票窗口,假设余票为100张,俩个售票窗口(线程)都在售票,并显示当前余票。使用线程互斥,为防止窗口1(线程1)到一半被窗口2(线程2)抢夺了资源,进行售票。 ...

    概要:利用多线程机制模拟售票过程。有俩个线程模拟售票窗口,假设余票为100张,俩个售票窗口(线程)都在售票,并显示当前余票。使用线程互斥,为防止窗口1(线程1)到一半被窗口2(线程2)抢夺了资源,进行售票。

    代码:

    // ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //
    
    #include <iostream>
    
    // 因为需要windows的API函数
    #include<Windows.h>
    
    DWORD WINAPI Fun2Pro(
        __in  LPVOID lpParameter
    );
    DWORD WINAPI Fun3Pro(
        __in  LPVOID lpParameter
    );
    
    int ticket = 100;
    HANDLE hMutex;
    
    int main()
    {
        int index = 0;
        // main 函数是主线程
        //创建线程
        HANDLE hThread1;
        HANDLE hThread2, hThread3;
        hThread2 = CreateThread(NULL, 0, Fun2Pro, NULL, 0, NULL);
        hThread3 = CreateThread(NULL, 0, Fun3Pro, NULL, 0, NULL);
        // 关闭句柄   关闭句柄,但没有关闭创建的线程
        // 只是关闭了主线程中此线程的句柄,是该线程的内核引用计数减1
    
        CloseHandle(hThread2);
        CloseHandle(hThread3);
    
        hMutex = CreateMutex(NULL,false,NULL);  // 创建一个匿名的互斥对象
    
        // 为防止子线程还未创建,主线程就退出可以添加 sleep() 让主线程暂停一小段时间
        Sleep(4000);  // 暂停当前线程 单位毫秒
        
        return 0;
    }
    
    // 模拟 俩个线程在同时售票
    DWORD WINAPI Fun2Pro(
       LPVOID lpParameter
    )
    {
        while (true)
        {
            WaitForSingleObject(hMutex,INFINITE);  // 申请互斥对像
            if (ticket > 0)
            { 
                std::cout << "线程1正在售票,余票为:" << ticket-- << "\n";
            }
            else
                break;
      
            //释放互斥对象的所有权
            ReleaseMutex(hMutex);
        }
        return 0;
    }
    DWORD WINAPI Fun3Pro(
         LPVOID lpParameter
    )
    {
        while (true)
        {
            WaitForSingleObject(hMutex, INFINITE);  // 申请互斥对像
            if (ticket > 0)
            {
                std::cout << "线程2正在售票,余票为:" << ticket-- << "\n";
            }
            else
                break;
    
        }
        return 0;
    }
    

    结果图:

    补充:

    添加互斥对象是因为:

    如果余票还有1时,售票窗口(线程1)正执行完判断余票是否>0的时候,时间片到期,转去执行售票窗口2(线程2),线程2将余票-1,此时余票转为0 ,在执行窗口1的时候,因为之前已经完成了判断余票的操作,即使此时余票为0,仍会执行下面的语句,这样会导致错误结果的发生。为防止这种情况,加入互斥对象。给每个线程要执行的语句加入互斥对像,当线程1在运行的时候,互斥信号为存在,其他的线程就不能执行。当线程1执行完,释放了互斥对象,线程2才能开始自己的执行。

    展开全文
  • 主要通过多线程来完成抢票系统package org.jsoft.thread;public class buy {public static void main(String[] args) {Mp mp=new Mp();//创建Mp类的对象Thread t1 = new Thread(mp, "黄牛1");Thread t2 = new Thread...
  • JAVA多线程总结

    2022-03-20 22:59:24
    首先我们要知道进程是系统进行资源分配和调度的基本单位,而线程是进程的一个执行路径,一个进程中至少有一个线程,进程中的线程共享进程的资源。 我们之前启动程序执行后,main方法的执行其实就是一条单独的...
  • Linux多线程

    千次阅读 2022-03-07 10:04:20
    线程一、线程是什么二、线程的优缺点线程的优点线程的缺点线程异常线程用途进程和线程对比三、线程的操作 一、线程是什么 在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的...
  • Java多线程教程(作者原创)

    千次阅读 多人点赞 2022-02-22 15:12:01
    Java多线程并发编程。
  • [linux] linux多线程详解

    千次阅读 2021-07-16 15:06:59
    主线程 pid = tgid 一个进程中绝对有一个主线程 工作线程 同一个进程中的线程的线程组是相同的,标识是同一个进程 但pid不同,标识不同的进程 注意:多线程在工作执行时,也是抢占式执行的,所有当有很多进程同时在...
  • 本书主要介绍在Java语言中使用多线程技术,首先介绍一下线程的概念:线程可以理解为在进程中独立执行的子任务,而进程是操作系统管理的基本运行单元。使用多线程技术,可以在同一时间内运行各种不同的任务,就好像你...
  • 随便谈谈多线程

    2021-08-15 18:46:12
    多线程基础 文章目录多线程基础前言一、pandas是什么?二、使用步骤1.引入库2.读入数据总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人...
  • 一个简单的用于微博私信的抢票程序,但主要是想记录一下自己程序不断优化的过程,并且分享一下
  • 多线程 详解(掌握这篇就够了)

    千次阅读 2021-03-18 10:30:05
    实现Callable接口4、龟兔赛跑5、小结继承Thread类实现Runnable接口静态代理Thread静态代理底层剖析动态代理newProxyInstance解析动态代理个接口动态代理的秘密代理的作用总结lambda表达式三、线程状态(五大状态)...
  • 一年一度的春运又来了,今年我自己写了个抢票脚本。使用Python+Splinter自动刷新抢票,可以成功抢到。(依赖自己的网络环境太厉害,还有机器的好坏)Splinter是一个使用Python开发的开源Web应用测试工具,它可以帮你...
  • Linux: 多线程

    千次阅读 2021-11-29 10:42:49
    线程概念: 线程是进程中的一条执行流程. 在linux之前学习进程的时候 ,进程就是一个pcb, 但是在现在学习线程的时候, 发现线程是进程中的一条执行流,而因为linux下执行流是通过pcb来完成的,所以理解pcb是linux...
  • 多线程线程安全问题

    2019-12-13 15:44:00
    线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。         经典案例...
  • 线程与多线程

    2022-03-16 21:06:18
    java基础多线程 1.1线程与进程 进程 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间 进程是系统进行资源分配和调度的一个独立单位. 线程 是进程中的一个执行路径,共享一个内存空间,线程之间...
  • python抢票程序

    2020-12-01 23:36:45
    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里技术人对外发布原创技术内容的最大平台;社区覆盖了云计算、大数据、人工智能、...
  • iOS 关于多线程

    2021-05-26 15:24:00
    一.进程和线程1.什么是进程进程是指在系统中...什么是线程1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行比如使用网易云音乐播...
  • //今晚内容:C语言-抢火车票软件//讲课老师:范志军 QQ:208824435#include ...//设定50张车票DWORD WINAPI Func(LPVOID lpParamter)//多线程的功能{while(a>0){WaitForSingleObject(hMutex,INFINITE);//等待执行...
  • 多线程和高并发介绍

    万次阅读 多人点赞 2021-06-06 05:00:46
    多线程和高并发介绍 文章目录多线程和高并发介绍前言一、什么是多线程?1.多线程介绍2.多线程实现原理?3.白话文解释多线程4.多线程存在的问题二、什么是高并发?1.高并发介绍2.如何提升系统的并发能力三、多线程和高...
  • java 多线程开发

    2022-01-04 10:03:12
    线程就是进程当中的一条执行路径,线程之间共享一个内存空间, 线程之间可以自由的切换, 并发执行。一个进程中可以线程,但是最少得有一个线程
  • 作者简介HongLiang,携程高级技术专家,专注系统性能、稳定性、承载能力和交易质量,在技术架构演进、高并发等领域有丰富的实践经验。一、背景去年疫情后,为了加速启动旅游市场,湖北在全域...
  • 多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。 多进程是指操作系统能同时运行多个任务(程序)。 多线程是指在同一程序中有多个顺序流在执行。 什么是线程什么是进程? 1)进程:每...
  • 高并发(High Concurrency)是一种系统运行过程中遇到的一种“短时间内遇到大量操作请求”的情况,主要发生在web系统集中大量访问收到大量请求(例如:12306的抢票情况;天猫双十一活动)。该情况的发生会导致系统在...
  • 高并发多线程简介

    2020-12-15 10:55:24
    高并发多线程简介 一、什么是高并发   高并发(High Concurrency)是一种系统运行过程中遇到的一种“短时间内遇到大量操作请求”的情况,主要发生在web系统集中大量访问收到大量请求(例如:12306的抢票情况;...
  • 高并发与多线程的关系与区别 liuhuiteng 2019-03-09 21:30:41 13357 收藏 80 分类专栏: Java进阶 文章标签: 高并发 多线程 高并发指标 高并发解决方案 一、什么是高并发 高并发(High Concurrency)是一种系统运行...
  • java多线程-基础

    2021-03-30 18:59:47
    java多线程基础 什么是进程和线程 进程:启动一个QQ.exe就叫一个进程。 接着又启动一个360.exe,这叫两个进程。以此类推,每个独立执行的程序都称为进程。可以从Windows任务管理器中查看 进程栏里 Java 给多线程编程...
  •   高伸缩性的并发编程是一种艺术,是成为高级程序员的必备知识点之一,最近总结了一下相关方面的知识。   借鉴过得博客有的我也不知道原文作业是谁 ... ... ...一、线程 ...什么是线程?...线程是进...
  • 多线程同步实现原理、同步函数、同步代码块、静态同步函数(关键字synchronized)...举个栗子:需求现在有100章火车票,有两个窗口同时抢火车票(相当于两个线程),请使用多线程模拟抢票效果。 代码如下:(此段代码非...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,511
精华内容 1,004
关键字:

多线程抢票技术