线程安全问题

目录

问题示范

 解决办法

同步代码块​

同步方法

Lock锁


卖票——三个窗口卖100张票

问题示范

public class Demo {
    public static void main(String[] args) {
        //这里Runnable接口实现类只能创建一个对象
        RunnableImpl runnable = new RunnableImpl();

        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        Thread t3 = new Thread(runnable);

        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        //启动线程
        t1.start();
        t2.start();
        t3.start();


    }
}
public class RunnableImpl implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {
        while (true){
            //票卖完了
            if (ticket == 0){
                break;
            }else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ticket--;
                System.out.println(Thread.currentThread().getName()+"在卖票,还剩下"+ticket+"张");
            }

        }
    }
}

这里输出结果如下:有重复票和复数;原因是线程在run方法处抢夺执行权,共同操作同一对象造成的线程安全问题

 解决办法

同步代码块

同步的好处和弊端,括号中也可以使用this代表当前的锁对象

  • 好处:解决了多线程的数据安全问题

  • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

public class Ticket implements Runnable {
    //票的数量
    private int ticket = 100;
    private Object obj = new Object();

    @Override
    public void run() {
        while(true){
            synchronized (obj){//多个线程必须使用同一把锁,保证多个线程只能操作同一个共享数据
                //如果是想创建多个线程操作同一数据,则需要共享数据为静态,锁对象静态唯一
                if(ticket <= 0){
                    //卖完了
                    break;
                }else{
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket--;
                    System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
                }
            }
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        /*Ticket ticket1 = new Ticket();
        Ticket ticket2 = new Ticket();
        Ticket ticket3 = new Ticket();

        Thread t1 = new Thread(ticket1);
        Thread t2 = new Thread(ticket2);
        Thread t3 = new Thread(ticket3);*/

        Ticket ticket = new Ticket();

        Thread t1 = new Thread(ticket);
        Thread t2 = new Thread(ticket);
        Thread t3 = new Thread(ticket);

        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        t1.start();
        t2.start();
        t3.start();
    }
}

同步方法

 静态同步方法和同步代码块共同使用一把锁

public class RunnableImpl implements Runnable{
    private static int ticket = 100;

    @Override
    public void run() {
        while (true){
            if ("窗口一".equals(Thread.currentThread().getName())){
                //同步方法
                boolean result = synchronizedMthod ();
                if (result){
                    break;
                }
            }
            if ("窗口二".equals(Thread.currentThread().getName())){
                //同步代码块
                synchronized (RunnableImpl.class){//这里要保持锁的一致
                    if (ticket==0){
                        break;
                    }else {
                        try {
                            Thread.sleep(12);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        ticket--;
                        System.out.println(Thread.currentThread().getName()+"在卖票,还剩下"+ticket+"张");
                    }
                }
            }
        }
    }

    //变成同步方法
    private synchronized static boolean synchronizedMthod() {
        if (ticket==0){
            return true;
        }else {
            try {
                Thread.sleep(12);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticket--;
            System.out.println(Thread.currentThread().getName()+"在卖票,还剩下"+ticket+"张");
            return false;
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        RunnableImpl mr = new RunnableImpl();
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);

        t1.setName("窗口一");
        t2.setName("窗口二");

        t1.start();
        t2.start();
    }
}

Lock锁

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

- ReentrantLock构造方法

  | 方法名             | 说明                   |
  | ReentrantLock() | 创建一个ReentrantLock的实例 |

- 加锁解锁方法

  | 方法名           | 说明   |
  | void lock()   | 获得锁  |
  | void unlock() | 释放锁  |

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值