线程安全问题的解决

解决线程的不安全问题

  • 1.同步代码块
synchronized(锁对象){
    可能会出现线程安全问题的代码块(访问了共享数据的代码块)
}
  • 注意:
    ①通过代码块中的锁对象,可以使用任何的对象
    ②但是必须保证多个线程使用的锁对象时同一个
    ③锁对象的作用:
    把同步代码块锁住,只让一个线程在同步代码块中执行
public class RunnableTicket implements Runnable {
    //定义一个线程共享的资源
    private int ticket=10;
   Object obj=new Object();
    @Override
    public void run() {
        //使用死循环,重复卖票操作
        while (true){
            synchronized (obj){
                if(ticket>0){
                    try {
                        Thread.sleep(100);//提高问题的出现率,便于观察,不写也可以
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
                    ticket--;
                }
            }
        }
    }
}

public class Demo02 {
    public static void main(String[] args) {
        //创建一个Runnable接口实现类对象
        // 注意:是一个不是多个,若是多个,则不存在资源共享问题
        RunnableTicket run=new RunnableTicket();
        //创建Thread类对象,构造方法中传入Runnable接口实现类对象
        //在此创建三个线程
        Thread t0=new Thread(run);
        Thread t1=new Thread(run);
        Thread t2=new Thread(run);
        //开启三个线程
        t0.start();
        t1.start();
        t2.start();
    }
}

观察图片可以看到,此时的售票系统正常,没有卖不存在的票,或者是重复卖同一张票
在这里插入图片描述

  • 同步技术的原理:
    ①使用了一个锁对象,这个对象叫同步锁,也叫对象锁,也叫对象监视器
    ②3个线程一起抢夺CPU的执行权,谁抢到谁执行run方法进行卖票
    ③t0抢到了CPU的执行权,执行run方法,遇到synchronized代码块,这是会检查synchronized代码块是否有锁对象,发现有,就会获取锁对象,进入同步代码中
    ④t1抢到了CPU的执行权,执行run方法,遇到synchronized代码块,这是会检查synchronized代码块是否有锁对象,发现没有,t1就进入阻塞状态,会一直等待t0线程把对象还给同步代码块,t1才能获取到锁对象进入到同步中执行
  • 总结:

    • 同步中的线程,没有执行完毕不会是否锁,同步外的线程没有锁进不去同步
    • 同步保证了只有一个线程在同步中执行共享数据,保证了安全,程序频繁的判断锁,获取锁,释放锁,程序的效率会降低
  • 2.同步方法
    ①把访问了共享数据的代码抽出来,放到一个方法中
    ②在方法上添加synchronized修饰符

 格式:修饰符 synchronized 返回值类型  方法名(参数列表){
 }
  • 定义一个同步方法,同步方法也会把方法内的代码所著,只让一个线程执行,
  • 普通同步方法锁的对象是谁?
    就是实现类对象–new RunnableTicket,也就是this
public class RunnableTicket implements Runnable {
    //定义一个线程共享的资源
    private int ticket=10;
    @Override
    public void run() {
        //使用死循环,重复卖票操作
        while (true) {
            method(ticket);//调用同步方法
        }
    }

	//1.普通同步方法
    private synchronized void method(int ticket) {
        if(ticket>0){
            try {
                Thread.sleep(100);//提高问题的出现率,便于观察,不写也可以
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
            ticket--;
        }
    }
    //2.静态同步方法--注意此时的ticket应该为static
    private synchronized void method(int ticket) {
        if(ticket>0){
            try {
                Thread.sleep(100);//提高问题的出现率,便于观察,不写也可以
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
            ticket--;
        }
    }
    }
  • 3.lock锁

Lock实现提供了比使用synchronized方法和话语可获得的更广泛的锁定操作 Lock接口中的方法:
void lock()获取锁
void unlock()释放锁

  • 使用步骤:
    ①在成员位置创建一个ReentrantLock对象
    ②在可能会出现问题的代码前调用Lock接口中的方法lock获取锁
    ③在可能会出现问题的代码后调用Lock接口中的方法unlock释放锁
public class RunnableTicket implements Runnable {
    //定义一个线程共享的资源
    private int ticket=10;
    Lock l=new ReentrantLock();
    @Override
    public void run() {
        //使用死循环,重复卖票操作
        while (true) {
            l.lock();
            if(ticket>0){
                try {
                    Thread.sleep(100);//提高问题的出现率,便于观察,不写也可以
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
                ticket--;
            }
            l.unlock();
        }
    }
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值