JavaEE | 线程安全(锁、线程间通信、内存可见性、CAS、线程的状态)

1.案例与线程安全

1.1 两种方式模拟电影院售票

案例:某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。

通过两种方式实现:
1.通过Runnable接口实现
2.通过Thread类实现

1.通过Runnable接口实现

public class MyTest1 {
   
    public static void main(String[] args) {
   
        //某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。
        //通过实现Runnable接口实现
        MyRunnable myRunnable = new MyRunnable();
        Thread th1 = new Thread(myRunnable);
        Thread th2 = new Thread(myRunnable);
        Thread th3 = new Thread(myRunnable);
        th1.setName("窗口1");
        th2.setName("窗口2");
        th3.setName("窗口3");
        th1.start();
        th2.start();
        th3.start();
    }
}

public class MyRunnable implements Runnable{
   
    private static int num=100;
    @Override
    public void run() {
   
        String name = Thread.currentThread().getName();
        while (true){
   
            if(num>=1){
   
                try {
   
                    // 售票时网络是不能实时传输的,总是存在延迟的情况,所以,在出售一张票以后,需要一点时间的延迟
                    // 改实现接口方式的卖票程序,每次卖票延迟100毫秒
                    Thread.sleep(100);
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
                System.out.println(name+"售出第"+(num--)+"张票");
            }else
            {
   
                break;
            }
        }
    }
}

2.通过Thread类实现

public class MyTest1 {
   
    public static void main(String[] args) {
   
        //某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。
        //通过继承Thread类实现
        MyThread th1 = new MyThread();
        MyThread th2 = new MyThread();
        MyThread th3 = new MyThread();
        th1.setName("窗口1");
        th2.setName("窗口2");
        th3.setName("窗口3");
        th1.start();
        th2.start();
        th3.start();
    }
}

public class MyThread extends Thread {
   
    //此处一定要写static,用来共享数据
    private static int num=100;

    @Override
    public void run() {
   
        String name = this.getName();
        while(true){
   
            try {
   
                Thread.sleep(50);
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            }
            if(num>1){
   
                System.out.println(name+"售出第"+(num--)+"张票");
            }
            else{
   
                break;
            }
        }
    }
}

注意:如果使用Thread类的继承类实现,共享变量(余票数量)一定要设置为静态变量
显然,本案例中,用Runnable接口实现的方式更好用。

1.2 买电影票出现了同票和负数票的原因分析

出现相同票的原因:是由于原子性所导致的。 "num–"不是一个原子性的操作,所谓原子性,指的是不可再分割。num–实际上有两步执行的,先运算,再自减。在num参与运算的过程中,如果有其他线程使用num会导致出现相同票。

出现0票或者负数票的原因:实际原因和出现相同票的原因相同,也是由于num–的非原子性所导致的。只是出现该问题的时间不同,当num=1时,多个线程同时进入循环,会导致粗线0或负值。

2. 保证线程安全之synchronized

2.1 概述

何种情况下会出现线程安全问题:

  1. 多线程环境
  2. 有共享数据
  3. 多条语句操作共享数据

分析:现在的这个售票程序是存在问题的,因为它满足上面的标准,那么我们只要将这个标准打乱,那么我们就可以解决这个问题.

而上面的标准中1 , 2是不能打乱的,因此我们只能对c做处理,关键是怎么处理? 如果我们把操作共享数据的多条语句看做成一个整体,当一个线程执行这个整体的时候,其他的线程处于等待状态,也就说当一个线程执行这个整体的时候,其他线程不能进行执行,那么怎么做到这个一点呢?

下面引入同步代码块,同步方法和同步静态方法。

2.2 同步代码块

格式:synchronized(对象){ //不能在括号了直接new 对象 new 了 就没效果
         要被同步的代码 ;
        }
  1. 这个同步代码块保证数据的安全性的一个主要因素就是这个对象被所有的线程对象所共享
  2. 这个对象要定义为静态成员变量,才能被所有线程共享
  3. 这个对象其实就是一把锁.
  4. 这个对象习惯叫做监视器

同步代码块的锁对象: 任意一个对象

public class MyTest1 {
   
    public static void main(String[] args) {
   
        
        MyRunnable myRunnable = new MyRunnable();
        Thread th1 = new Thread(myRunnable);
        Thread th2 = new Thread(myRunnable);
        Thread th3 = new Thread(myRunnable);
        th1.setName("窗口一");
        th2.setName("窗口二");
        th3.setName("窗口三");
        th1.start();
        th2.start();
        th3.start();
    }
}


public class MyRunnable implements Runnable {
   
    static int num=100;
    static Object obj=new Object();
    @Override
    public void run() {
   
        String name = Thread.currentThread().getName();
        while (true)
        {
   
            synchronized (obj){
   
                //锁 ,其实就是一个任意对象,多个线程要共享一把锁
                if(num>=1){
   
                    try {
   
                        Th
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值