Java中使用synchronizedg关键字解决抢票超卖问题

本文为joshua317原创文章,转载请注明:转载自joshua317博客 Java中使用synchronizedg关键字解决抢票超卖问题 - joshua317的博客

一、多线程问题

我们先来看下在没有synchronized使用的情况下,如果不加锁机制,多线程卖票会出现什么问题?

package com.joshua317;

public class Ticket extends Thread{
    private static int ticketNums = 100;
    public static void main(String[] args) {
        Ticket ticket1 = new Ticket();
        Ticket ticket2 = new Ticket();
        Ticket ticket3 = new Ticket();

        ticket1.start();
        ticket2.start();
        ticket3.start();
    }

    @Override
    public void run() {
        while (true) {
            if (ticketNums <=0) {
                System.out.println("票已经售罄!");
                break;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("售票窗口:" + Thread.currentThread().getName() + " 售出一张票," + "剩余票数为:" + (--ticketNums));
        }
    }
}

上面例子我们可以发现,当多线程运行时,会导致出现负数的情况,也就是卖超了。原因就是线程不同步。同一进程的多个线程Thread-0、Thread-1、Thread-2共享同一片存储空间,它们同时看见票仅剩一张,它们同时去抢,当某个线程抢到时,其他线程已经过了if判断票数的代码,于是最后一张被抢走时,票已经变为-1或者-2。

接下来我们通过创建线程的俩种方式,来使用synchronized关键字,看下synchronized的用法

二、使用synchronized同步代码块解决线程加锁

2.1 通过继承Thread来创建线程,使用synchronized

package com.joshua317;

public class Ticket extends Thread{
    private static int ticketNums = 100;
    public static void main(String[] args) {
        Ticket ticket1 = new Ticket();
        Ticket ticket2 = new Ticket();
        Ticket ticket3 = new Ticket();

        ticket1.start();
        ticket2.start();
        ticket3.start();
    }

    @Override
    public void run() {
        while (true) {
            //方法中的代码块,使用synchronized,注意此时同步锁锁定的对象是Ticket.class
            synchronized (Ticket.class) {
                if (ticketNums <=0) {
                    System.out.println("票已经售罄!");
                    break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("售票窗口:" + Thread.currentThread().getName() + " 售出一张票," + "剩余票数为:" + (--ticketNums));
            }
        }
    }
}

2.2 通过实现 Runnable 接口来创建线程,使用synchronized

package com.joshua317;

public class TicketRun implements Runnable{
    private static int ticketNums = 100;
    public static void main(String[] args) {
        TicketRun ticket = new TicketRun();

        Thread thread1 = new Thread(ticket);
        Thread thread2 = new Thread(ticket);
        Thread thread3 = new Thread(ticket);

        thread1.start();
        thread2.start();
        thread3.start();
    }

    @Override
    public void run() {
        while (true) {
            //方法中的代码块,使用synchronized,注意此时同步锁锁定的对象是this
            synchronized (this) {
                if (ticketNums <=0) {
                    System.out.println("票已经售罄!");
                    break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("售票窗口:" + Thread.currentThread().getName() + " 售出一张票," + "剩余票数为:" + (--ticketNums));
            }
        }
    }
}

三、Java synchronized关键字

在Java中,同步代码块被synchronized关键字标记。Java中的同步块是在某个对象上同步。synchronized 关键字声明的方法或者代码块同一时间只能被一个线程访问,也就是说同一时间所有的在同一个对象上同步的同步块只能被一个线程进入执行。所有其他尝试进入同步块的线程都将被阻塞直到在同步块内执行的线程退出这个同步块。

缺点就是:使用synichronized 将会影响效率。

3.1 代码块使用synchronized

synchronized(Obj){}

Obj 称为同步监视器,它可以是任何对象,但一般是共享资源。

同步方法中无需指定同步监视器,因为同步方法的同步监视器就是这个对象本身或者是class。

【例如 有class A,在class A中有同步方法,而在A的同步方法是中的 this 就是同步监视器】

同步监视器执行过程:

第一个线程访问,锁定同步监视器,执行{ }中的代码 第二个线程访问,发现同步监视器被锁定,无法访问 第一个线程执行结束,解锁同步监视器 第二个线程访问,同步监视器未锁,然后锁定并执行{ }其代码

3.2 类方法使用synchronized

public synchronized void methordName(){}

注意methordName()方法声明中使用synchronized关键字告诉 Java 该方法是同步的。

Java 中的同步实例方法在拥有该方法的实例(对象)上进行同步。因此,每个实例对象的实例方法将在不同对象(拥有实例方法的实例对象本身)上进行同步。每个实例只有一个线程可以在同步实例方法中执行。如果存在多个线程,则每一个实例对象,同一时刻只有一个线程可以进入其同步实例方法中执行。即每个实例一个线程。

本文为joshua317原创文章,转载请注明:转载自joshua317博客 Java中使用synchronizedg关键字解决抢票超卖问题 - joshua317的博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
订单库存存在超卖问题,通常是由于多线程或分布式环境下对于共享资源的并发访问导致的。在Java,可以通过以下几种方式来解决订单库存存在超卖问题: 1. 悲观锁:即对于并发访问的共享资源,认为每次访问都会产生冲突,因此在访问共享资源之前先对其进行加锁,阻塞其他的访问请求。在Java,可以使用synchronized关键字或ReentrantLock类来实现悲观锁。 2. 乐观锁:即对于并发访问的共享资源,认为每次访问都不会产生冲突,因此不对其进行加锁,而是在访问共享资源之前先获取其版本号或时间戳,然后在更新共享资源时进行版本号或时间戳的比较,如果发现冲突,则进行回滚或重试。在Java,可以使用Atomic类或版本号机制来实现乐观锁。 3. 分布式锁:即对于分布式环境下的共享资源,使用分布式锁来保证其互斥访问。分布式锁可以基于ZooKeeper或Redis等分布式协调服务来实现。 4. 库存预占:即在用户提交订单时,先对订单所包含的商品进行库存预占,然后在订单支付成功后再进行库存扣减。这种方式可以避免多个用户同时下单导致库存超卖问题,但需要注意预占的库存要及时释放,否则会导致库存浪费。 以上几种方式都可以用来解决订单库存存在超卖问题,具体选择哪种方式需要根据实际情况进行综合考虑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值