Java中的可重入锁ReentrantLock以及synchronized实现多人抢票

在前面的抢票中出现了多个人抢到了同一张票,还有负数。可以发现多线程出现了问题。这是由于同一进程的多个线程共享一块存储空间,在带来方便的时候,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入了锁机制synchronized当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁。判断一个多线程是否有问题的标准:

  • 是否是多线程环境
  • 是否存在共享数据
  • 是否存在多条语句同时操作共享数据
解决多线程安全
1.同步方法

  针对方法提出一套机制 , 这套机制就是 synchronized 关键字他有两种用法synchronized 方法 和synchronized 块。
同步方法

public synchronized void method(int args) {}

  synchronized方法控制对 “对象” 的访问 , 每个对象对应一把锁 , 每个synchronized方法都必须获得调用该方法的对象的锁才能执行 , 否则线程会阻塞 ,方法一旦执行 , 就独占该锁 , 直到该方法返回才释放锁 , 后面被阻塞的线程才能获得这个锁 , 继续执行
同步方法的锁对象: 是this
静态同步方法的锁对象:就是当前类对应的字节码文件对象

同步方法的弊端

优点:同步的出现解决了线程安全问题
缺点:当线程过多时,因为每个线程都会去判断同步上的锁,会很耗费资源,从而大大的降低程序的运行效率。

public class Ticket implements Runnable {
    private int ticketNums = 10;
    private boolean flag = true;
    @Override
    public  void run() {
        while (flag){
           take();
        }
    }
    private synchronized void take() {

        if(ticketNums<=0){
            flag = false;
            return;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNums-- +"张票");
    }

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(ticket,"张三").start();
        new Thread(ticket,"李四").start();
        new Thread(ticket,"王五").start();
    }
}

运行结果为:

张三抢到了第10张票
张三抢到了第9张票
张三抢到了第8张票
张三抢到了第7张票
张三抢到了第6张票
张三抢到了第5张票
王五抢到了第4张票
李四抢到了第3张票
王五抢到了第2张票
张三抢到了第1张票
2.同步块

同步代码块的格式

synchronized(Obj){
		需要同步的代码;
	}

Obj称之为同步监视器,
1.Obj可以时任何对象,但是推荐使用共享资源作为同步监视器
2.同步方法中无需指定监视器,因为同步方法的同步监视器就是this,这个对象本身。
同步监视器的执行过程

  1. 第一个线程访问,锁定同步监视器,执行其中代码
  2. 第二个线程访问,发现同步监视器被锁定,无法访问
  3. 第一个线程访问完毕,解锁同步监视器
  4. 第二个线程访问,发现同步监视器没有锁,然后锁定进行访问
public class Ticket2 implements Runnable {
    private int ticketNums = 10;
    private boolean flag = true;
    private static final Object obj = new Object() ;
    @Override
    public  void run() {
        while (flag){
           take();
        }
    }
    private  void take() {
        synchronized (obj){
            if(ticketNums<=0){
                flag = false;
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNums-- +"张票");
        }
    }

    public static void main(String[] args) {
        Ticket2 ticket = new Ticket2();
        new Thread(ticket,"张三").start();
        new Thread(ticket,"李四").start();
        new Thread(ticket,"王五").start();
    }
}

2.Lock锁

Lock锁:在上面的同步代码块和同步方法锁对象,但是没有看见哪里上了锁,哪里释放了锁,为了更清晰的表达如何加锁以及释放锁,JDK1.5之后提供了一个新的锁对象Lock


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Ticket1 implements Runnable {
    private int ticketNums = 10;
    private boolean flag = true;
    //创建所对象
    private static final Lock lock = new ReentrantLock() ;
    @Override
    public  void run() {
        while (flag){
            take();
        }
    }
    private void take() {
        //添加锁
        lock.lock();
        if(ticketNums<=0){
            flag = false;
            return;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNums-- +"张票");
        //释放锁
        lock.unlock();
    }

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(ticket,"张三").start();
        new Thread(ticket,"李四").start();
        new Thread(ticket,"王五").start();
    }
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值