线程的安全问题

同步代码块解决线程安全

使用同步代码块解决线程安全,使用静态变量作为全局共享变量

//设场景,3个人在分发100包标有号码的小礼品,分完停止 代码如下:

package com.ruoyi.web.controller.thread;

import org.apache.poi.ss.formula.functions.T;

/**
 * @author zhuang.bq
 * @create 2024/5/16  14:53
 * @desc
 **/
public class MyThread6 extends Thread {

    //标识这个类所有的对象 都共享该参数
    static int num = 0;// 0-99

    public MyThread6() {
    }

    public MyThread6(String name) {
        super(name);
    }

    @Override
    public void run() {
        while (true) {
            if (num < 100) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                num++;
                System.out.println(getName() + "正在分发" + num + "包");
            } else {
                break;
            }
        }
    }

    /**
      * 设场景,3个人在分发标有号码的100包小礼品,分完停止
     * @param args
     */
    public static void main(String[] args){
        MyThread6 t1 = new MyThread6("员工1-");
        MyThread6 t2 = new MyThread6("员工2-");
        MyThread6 t3 = new MyThread6("员工3-");
        t1.start();
        t2.start();
        t3.start();
    }
}

执行结果如下:会发现线程不安全导致不同员工(线程)分发了同一个号码的礼品,即相同的号码数出现了多次,且超出了范围号码

使用同步代码块 可以解决多线程抢占同一个资源的问题 即优化后的代码 但是同步代码块只能锁住共享对象,但是不能控制线程的执行顺序

package com.ruoyi.web.controller.thread;


/**
 * @author zhuang.bq
 * @create 2024/5/16  14:53
 * @desc
 **/
public class MyThread6 extends Thread {

    //标识这个类所有的对象 都共享该参数 之所以加上static 是因为我们会多次创建MyThread类 所以需要加上static标识,往下 我们用Runable就可以不用了。
    static int num = 0;// 0-99
    static Object object = new Object();//锁对象一定要是唯一的 锁不是唯一 会一个线程一个锁 就没有意义了
    public MyThread6() {
    }

    public MyThread6(String name) {
        super(name);
    }

    @Override
    public void run() {
        while (true) {
            //同步代码块
            synchronized (object) { //不能让synchronized同步代码块锁在while外面,否则会变成只能让一个线程来分发
           // synchronized (MyThread6.class) { //也可以用当前类的字节码锁住
                //特点1:锁默认打开,有一个线程进去了,锁自动关闭
                //特点2:里面的代码全部执行完毕,线程出来,锁自动打开
                if (num < 100) {
                    try {
                        Thread.sleep(10); //线程执行有随机性 cpu被抢走 导致数据被抢占
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    num++;
                    System.out.println(getName() + "正在分发" + num + "张");
                } else {
                    break;
                }
            }

        }
    }

    /**
     * 设场景,3个人分发100张粮票,分完停止
     *
     * @param args
     */
    public static void main(String[] args) {
        MyThread6 t1 = new MyThread6("员工1-");
        MyThread6 t2 = new MyThread6("员工2-");
        MyThread6 t3 = new MyThread6("员工3-");
        t1.start();
        t2.start();
        t3.start();
    }
}

--使用lock锁 手动处理优化上面问题

同步方法解决线程安全

特点1:同步方法是锁住方法里面所有的代码

特点2:所对象不能自己指定

非静态:this 静态:当前类的字节码文件对象

package com.ruoyi.web.controller.thread;

/**
 * 实现runnable接口方式实现
 *
 * @author zhuang.bq
 * @create 2024/5/13  15:57
 * @desc
 **/
public class MyRun3 implements Runnable {


    int num = 0;//与继承Thread不同的是,实现Runnable无需多次创建MyRun类 创建一个对象之后 我们实例化的时候 num值即为全局变量 无需加上static

    @Override
    public void run() {
        //1.循环
        //2.同步代码块 然后放入同步方法中
        //3.判断共享数据未到末尾
        //4.判断共享数据到末尾了
        while (true) {
            if (deal()) {
                break;
            }
            ;

        }
    }

    //this
    private synchronized boolean deal() {
        if (num == 100) {
            return true;
        } else {
            try {
                Thread.sleep(10); //防止一个线程全部执行完
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            num++;
            System.out.println(Thread.currentThread().getName() + "_在分发第" + num + "张粮票!");
        }
        return false;
    }

    /**
     * 设场景,3个人分发100张粮票,分完停止
     *
     * @param args
     */
    public static void main(String[] args) {

        MyRun3 myRun3 = new MyRun3();//创建run对象 表示需要执行的任务 myRun3 == this
        Thread t1 = new Thread(myRun3);//创建线程对象,并将自定义线程丢进去
        Thread t2 = new Thread(myRun3);
        Thread t3 = new Thread(myRun3);
        t1.setName("员工1:");
        t2.setName("员工2");
        t3.setName("员工3");
        t1.start();//开启线程
        t2.start();
        t3.start();

    }


}

ps:以上代码是通synchronized 自动锁,在进入synchronized之后 自动加锁,处理完之后 自动解锁

使用lock锁 手动锁定 解决Thread继承超额分配同时分配问题
package com.ruoyi.web.controller.thread;


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

/**
 * @author zhuang.bq
 * @create 2024/5/16  14:53
 * @desc
 **/
public class MyThread7 extends Thread {

    //标识这个类所有的对象 都共享该参数
    static int num = 0;// 0-99
    static Object object = new Object();//锁对象一定要是唯一的 锁不是唯一 会一个线程一个锁 就没有意义了
    static Lock lock = new ReentrantLock(); //防止多次创建 生成多把锁

    public MyThread7() {
    }

    public MyThread7(String name) {
        super(name);
    }

    @Override
    public void run() {
        try {
            while (true) {
                lock.lock();
//            synchronized (object) {
                if (num < 100) {
                    Thread.sleep(10); //线程执行有随机性 cpu被抢走 导致数据被抢占

                    num++;
                    System.out.println(getName() + "正在分发" + num + "张");
                } else {
                    break;
                }
//            }
//                lock.unlock();//如果放在while里面 会使得其他线程无法获得锁
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    /**
     * 设场景,3个人在分发100张粮票,分完停止
     *
     * @param args
     */
    public static void main(String[] args) {
        MyThread7 t1 = new MyThread7("员工1-");
        MyThread7 t2 = new MyThread7("员工2-");
        MyThread7 t3 = new MyThread7("员工3-");
        t1.start();
        t2.start();
        t3.start();
    }
}

自己使用锁 可能会发生死锁

        ·多个线程同时抢到同一把锁 不释放资源的情况下 会出现死锁

        下面就是错误示范 尽量别让两个锁嵌套即可

package com.ruoyi.web.controller.thread;


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

/**
 * @author zhuang.bq
 * @create 2024/5/16  14:53
 * @desc
 **/
public class MyThread8 extends Thread {

    static Object objA = new Object();
    static Object objB = new Object();
    public MyThread8() {
    }

    public MyThread8(String name) {
        super(name);
    }

    @Override
    public void run() {
        while (true){
            if("线程A".equals(getName())){
                synchronized (objA){
                    System.out.println("线程A拿到A锁,准备拿B锁!");
                    synchronized (objB){
                        System.out.println("线程A拿到B锁,顺利执行完成一轮!");
                    }
                }
            } else if("线程B".equals(getName())){
                synchronized (objB){
                    System.out.println("线程B拿到B锁,准备拿A锁!");
                    synchronized (objA){
                        System.out.println("线程B拿到A锁,顺利执行完成一轮!");
                    }
                }
            }
        }
    }


    /**
     * 设场景,3个人在分发100张粮票,分完停止
     *
     * @param args
     */
    public static void main(String[] args) {
        MyThread8 t1 = new MyThread8("线程A");
        MyThread8 t2 = new MyThread8("线程B");
        t1.start();
        t2.start();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zbqice007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值