线程安全问题

线程安全-同步方法-Runnable

解决线程安全问题

思路:加锁
SE阶段,提供一个同步锁,关键字synchronized,
它可以给一段代码加锁,同一时刻,只允许它监视的多个线程中的一个线程执行这段代码。

同步的语法格式有两种:
(1)同步方法
【其他修饰符】synchronized 返回值类型 方法名(【形参列表】)【throws 异常列表】{
方法体代码;
}

锁的是方法体代码。

(2)同步代码块

同步锁注意的问题

(1)范围锁太大,不行
会导致其他线程没有机会

(2)范围锁太小,也不行
会导致锁的不彻底
例如:条件判断没锁进去,条件中也用了共享数据

(3)同步锁对象的选择问题
所选择的对象必须各个线程共用。

5、同步方法是如何选择锁对象的呢?

非静态方法:默认的锁对象是this对象
静态方法:默认的锁对象是 当前类对象(当前类的Class对象)

package com.atguigu.safe;
public class TestThreadSafe3 {
    public static void main(String[] args) {
        Demo3 demo3 = new Demo3();
        Thread t1 = new Thread(demo3,"窗口1");
        Thread t2 = new Thread(demo3,"窗口2");
        Thread t3 = new Thread(demo3,"窗口3");
        //3个Thread线程对象,共享同一个Demo3的对象,堆内存中的对象可以被多个线程共享

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

/*
总票数假设是10张票,分为3个窗口一起抢
 */
/*
class Demo3 implements Runnable{
    private int total = 100;//total如果太小的话可能会出现只有一个窗口卖票
    						//如果是实例变量,每一个对象都是独立的
                            //如果只有一个Demo3的对象,那么total就只有一个

    @Override
    public synchronized void run() { //范围锁太大了
        while(total>0){
            try {
                Thread.sleep(1000);//这句代码的作用是让问题暴露的明显一点
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            total--;
            System.out.println(Thread.currentThread().getName() + "卖了一张票,剩余" + total +"张票");
        }
    }
}*/
/*class Demo3 implements Runnable{
    private int total = 10;//如果是实例变量,每一个对象都是独立的
    //如果只有一个Demo3的对象,那么total就只有一个

    @Override
    public void run() {
        while(total>0){
            saleOneTicket();//之前判断完条件,可能因为等锁的过程中,total会被其他线程修改
        }
    }

    //卖一张票
    private synchronized void saleOneTicket(){//范围锁太小了,没有包含条件
        try {
            Thread.sleep(1000);//这句代码的作用是让问题暴露的明显一点
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        total--;
        System.out.println(Thread.currentThread().getName() + "卖了一张票,剩余" + total +"张票");
    }
}*/
class Demo3 implements Runnable{
    private int total = 1000;//如果是实例变量,每一个对象都是独立的
    //如果只有一个Demo3的对象,那么total就只有一个

    @Override
    public void run() {
        while(total>0){
            saleOneTicket();//之前判断完条件,可能因为等锁的过程中,total会被其他线程修改
        }
    }

    //卖一张票
    //非静态方法,锁对象是this
    //这里的this就是Demo3的对象,从头到尾3个线程使用的是同一个Demo3对象,即锁对象是同一个
    private synchronized void saleOneTicket(){//范围锁太小了,没有包含条件
        if(total>0) {//total值是多个线程共享的,所以进入同步方法之后,重新判断total值
            try {
                Thread.sleep(10);//这句代码的作用是让问题暴露的明显一点
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            total--;
            System.out.println(Thread.currentThread().getName() + "卖了一张票,剩余" + total + "张票");
        }else{
            return;
        }
    }
}

线程安全-同步方法-Thread

静态方法不能调用父类的非静态方法

package com.dudu.day0322;

/**
 * @author:嘟嘟
 * @date:2022/3/23
 */
public class SafeThreadTest {
    public static void main(String[] args) {
        Demo1 d1=new Demo1("窗口1");
        Demo1 d2=new Demo1("窗口2");
        Demo1 d3=new Demo1("窗口3");
        d1.start();
        d2.start();
        d3.start();
    }
}
class Demo1 extends Thread{
    private static int total=100;

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

    @Override
    public void run() {
        while (true) {
            safe();
        }
    }
    public synchronized static void safe(){
        if (total>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            total--;
            System.out.println(Thread.currentThread().getName()+"卖了一张票、还剩"+total+"张票");
        }else{
            return;
        }
    }
}

线程安全-同步代码块-Runnable

同步的语法格式:同步代码块

synchronized(锁对象){
要加锁的代码
}

注意的问题:
范围不能太大、太小。
锁对象的选择必须是同一个锁对象

package com.dudu.day0322;

/**
 * @author:嘟嘟
 * @date:2022/3/23
 */
public class SafeTest1 {
    public static void main(String[] args) {
        Demo2 demo = new Demo2();
        Thread t1=new Thread(demo,"窗口1");
        Thread t2=new Thread(demo,"窗口2");
        Thread t3=new Thread(demo,"窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}
class Demo2 implements Runnable{
    private int total=100;
    @Override
    public void run() {
        while (total>0){
            synchronized(this){
                if (total>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    total--;
                    System.out.println(Thread.currentThread().getName()+"卖了一张票、还剩下"+total+"张票");
                }
            }
        }
    }
}

线程安全-同步代码块-Thread

package com.dudu.day0322;

/**
 * @author:嘟嘟
 * @date:2022/3/23
 */
public class SafeThreadTest {
    public static void main(String[] args) {
        Demo1 d1=new Demo1("窗口1");
        Demo1 d2=new Demo1("窗口2");
        Demo1 d3=new Demo1("窗口3");
        d1.start();
        d2.start();
        d3.start();
    }
}
class Demo1 extends Thread{
    private static int total=100;

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

    @Override
    public void run() {
        while (total>0) {
            //synchronized(this)这里的this指的是Demo1对象、有三个
            synchronized(this.getClass()){
                //this.getClass()、对应的类是同一个
                if (total>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    total--;
                    System.out.println(Thread.currentThread().getName()+"卖了一张票、还剩"+total+"张票");
                }
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值