八月三日上午笔记多线程初步实现的思路(synchronized锁的应用)

多线程的实现

//100张电影票,三个窗口售票
//三个窗口售票,互不影响
//三个窗口共同出售这100张电影票
//通过线程的实现方式一 和 线程的实现方式二 售票效果的比较,得出的结论 :实现方式二比实现方式一更容易实现多线程的数据


public class Day80301 {
    public static void main(String[] args) {
//        //创建3个SalesWindow对象,表示三个线程,模拟三个售票窗口
//        SalesWindow w1 = new SalesWindow();
//        SalesWindow w2 = new SalesWindow();
//        SalesWindow w3 = new SalesWindow();
//
//        //启动三个线程,开始售票
//        w1.start();
//        w2.start();
//        w3.start();
        SalesTask salesTask = new SalesTask();
        Thread thread1= new Thread(salesTask);
        Thread thread2 = new Thread(salesTask);
        Thread thread3 = new Thread(salesTask);
        thread1.start();
        thread2.start();
        thread3.start();

    }
}
//线程的第一种实现方式

class SalesWindow extends  Thread{
    //表示100张票
    int  tickets = 100;

    @Override
    public void run() {
        while (tickets>0){

            //输出语句,模拟售出一张票
            System.out.println(getName()+":售出了第"+tickets-- + "张票");

        }
    }
}
class SalesTask implements Runnable{

    int tickets = 100;
    @Override
    public void run() {

        while (tickets>0){
            System.out.println(Thread.currentThread().getName()+":售出了第:"+tickets--+"张票");
        }
    }
}

//当更加真实的模拟,售票场景,即增加了售票延迟之后,我们发现了两个问题
//1.售出了同一张票(多卖问题)
//Thread-2:售出了第:13张票
//Thread-1:售出了第:13张票
//Thread-0:售出了第:13张票
//2.售出了不存在的票(超卖问题)
//其实不管是多卖,还是超卖,他们都属于多线程的数据安全问题。
// 在多线程环境下,多线程访问共享数据时,访问到不正确的共享数据的值



public class Day80302 {
    public static void main(String[] args) {
//        //创建3个SalesWindow对象,表示三个线程,模拟三个售票窗口
//        SalesWindow1 w1 = new SalesWindow1();
//        SalesWindow1 w2 = new SalesWindow1();
//        SalesWindow1 w3 = new SalesWindow1();
//
//        //启动三个线程,开始售票
//        w1.start();
//        w2.start();
//        w3.start();
        SalesTask1 salesTask = new SalesTask1();
        Thread thread1= new Thread(salesTask);
        Thread thread2 = new Thread(salesTask);
        Thread thread3 = new Thread(salesTask);
        thread1.start();
        thread2.start();
        thread3.start();

    }
}
//线程的第一种实现方式

class SalesWindow1 extends  Thread{
    //表示100张票
  static   int  tickets = 100;

    @Override
    public void run() {
        while (tickets>0){

            //输出语句,模拟售出一张票
            System.out.println(getName()+":售出了第"+tickets-- + "张票");

        }
    }
}
class SalesTask1 implements Runnable{

    int tickets = 100;
    @Override
    public void run() {

        //假设当前tickets的值是1
        // thread3 :当前tickets的值是1, 1>0 ,thread3 进入循环准备卖票,此时发生了线程切换
        // thread2 : 当前tickets的值是1, 1>0, thread2进入循环准备卖票,此时发生了线程切换
        // thread1 : 当前tickets的值是1, 1>0,thread1 进入循环准备卖票


        while (tickets>0){




            //每次售票的延迟
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //当前tickets的值是99
            //多卖问题 :前提:tickets--:     1). 读取tickets 变量的值
            // 2). 减一  tickets - 1      3) tickets = tickets - 1
            //thread2 :拼接字符串, thread2: 售出了第97 若此时发生了线程切换
            //thread3 : 拼接字符串: thread3: 售出了第97, 此时发生了线程切换
            //thread1 : 拼接字符串窜 :thread1:售出了第97张票




            System.out.println(Thread.currentThread().getName()+":售出了第:"+tickets--+"张票");
        }
    }
}

在这里插入图片描述
//1.多线程运行环境
//2.多线程访问共享数据
//3.共享数据访问的非原子操作。
// 原子操作通常是指这样一组操作: 要么一组操作一次完成,要么一个操作都不做,即一组不可分割的操作
//如何解决? 只要打破上述任意条件即可
// 1. 多线程条件,无法打破(需求决定)
//2. 访问共享数据条件,(需求决定,无法打破)
//3.共享数据的费原子操作,可以打破
// 所以解决多线程数据安全问题 —> 如何将一组对共享变量的访问,变成原子操作的问题
// 思路1 : 阻止线程切换,(是由操作系统控制的,但是我们自己无法控制操作系统,抢占式线程调度)此思路无法实现
// 思路2 : 给共享变量加一把锁,利用锁来实现原子操作
// a.只有加锁的线程,才能够访问到共享变量
// b.不完成对共享变量的操作,不会释放锁
// c.只要不释放锁,其他线程就无法访问到共享变量
//synchronized (锁对象){
// 将需要作为原子操作的一组操作,放入synchronized代码块中,(需要同步的代码块)
//这一组操作就变成了原子操作
// }



public class Day80303 {
    public static void main(String[] args) {
        SalesTask2 salesTask1 = new SalesTask2();
        Thread thread1= new Thread(salesTask1);
        Thread thread2 = new Thread(salesTask1);
        Thread thread3 = new Thread(salesTask1);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class SalesTask2 implements Runnable {

    int tickets = 100;

    private Object lock = new Object();

    @Override
    public void run() {

        //假设当前tickets的值是1
        // thread3 :当前tickets的值是1, 1>0 ,thread3 进入循环准备卖票,此时发生了线程切换
        // thread2 : 当前tickets的值是1, 1>0, thread2进入循环准备卖票,此时发生了线程切换
        // thread1 : 当前tickets的值是1, 1>0,thread1 进入循环准备卖票

        while (tickets > 0) {


            //每次售票的延迟
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //当前tickets的值是99
            //多卖问题 :前提:tickets--:     1). 读取tickets 变量的值
            // 2). 减一  tickets - 1      3) tickets = tickets - 1
            //thread2 :拼接字符串, thread2: 售出了第97 若此时发生了线程切换
            //thread3 : 拼接字符串: thread3: 售出了第97, 此时发生了线程切换
            //thread1 : 拼接字符串窜 :thread1:售出了第97张票

            //将以此售票过程,变成一组原子操作
            synchronized (lock) {
                if (tickets > 0)  {//双检查 (double check)
                    System.out.println(Thread.currentThread().getName() + ":售出了第:" + tickets-- + "张票");
                }

            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值