Java多线程——线程安全问题与线程同步&死锁

线程安全问题

  • 买火车票问题
package com.thread.demo;

public class Demo3 implements Runnable {
/*  买火车票问题
* 多个线程操作同一个资源时,出现线程安全问题,发生数据紊乱
* */
    private int ticketNums = 10;

    @Override
    public void run() {
        while (true) {
            if (ticketNums <= 0)
            {
                break;
            }
            try {
                Thread.sleep(200); // 模拟延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张票");
        }
    }

    public static void main(String[] args) {

        Demo3 demo3 = new Demo3();

        new Thread(demo3,"小明").start();
        new Thread(demo3,"小军").start();
        new Thread(demo3,"黄牛").start();
    }

}

线程同步

  • 并发:同一个对象被多个线程被同时操作
    • 上万人同时抢100张票
    • 两个银行同时取钱
  • 可以通过 synchronized关键字对方法或者代码块进行修饰,让其成为同步方法或者同步代码块
package com.thread.demo;

public class Demo17 implements Runnable {
    /*  买火车票问题
     * 多个线程操作同一个资源时,出现线程安全问题,发生数据紊乱
     * */
    private int ticketNums = 100;

    @Override
    public void run() {
        while (true) {
            if (buyTickets()) break;
        }
    }

    private synchronized boolean buyTickets() {
//        买票
        if (hasTicketsOne()) return true; //判断是否有票
        sleepSomeTime(); // 模拟延时
        System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张票");
        return false;
    }

    private void sleepSomeTime() {
        try {
            Thread.sleep(50); // 模拟延时
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private boolean hasTicketsOne() {
//        判断是否有票
        if (ticketNums <= 0)
        {
            return true;
        }
        return false;
    }

    public static void main(String[] args) {

        Demo17 demo17 = new Demo17();

        Thread you = new Thread(demo17, "牛逼的你们");
        Thread me = new Thread(demo17, "苦逼的我");
        Thread tmd = new Thread(demo17, "可恶的黄牛党");

        you.setPriority(Thread.NORM_PRIORITY);
        tmd.setPriority(8);
        me.setPriority(Thread.MIN_PRIORITY);

        you.start();
        me.start();
        tmd.start();
    }


}
  • 同步方法虽然使得线程变得安全了,但是运行效率大大降低!!

  • 所以我们只对发生改变的那一块上锁

  • 扩充—JUC安全类型的集合

    package com.thread.demo;
    
    import java.util.concurrent.CopyOnWriteArrayList;
    
    /*测试JUC安全类型集合*/
    public class Demo18 {
        public static void main(String[] args) throws InterruptedException {
    
            CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
            for (int i = 0; i <10000 ; i++) {
                new Thread(()->{
                 list.add(Thread.currentThread().getName());
                }).start();
            }
            Thread.sleep(3000);
            System.out.println(list.size());
        }
    }
    

死锁

  • 死锁:多个线程互相抱着对方需要的资源,形成僵持
  • 产生死锁的四个必要条件:
    1. 互斥条件:一个资源每次只能被一个进程所使用
    2. 请求与保护条件:一个进程因请求资源而阻塞时,对已获取的资源保持不放
    3. 不剥夺条件:进程已获得资源,再未使用完之前,不能强行剥夺。
    4. 循环等待条件:若干进程之间形成一种头尾相接等待资源的关系。
  • 避免死锁只要想办法破坏其中一个或多个条件即可避免。
package com.thread.demo;

public class Demo19 {

    public static void main(String[] args) {
        Child child1 = new Child("小明", 1);
        Child child2 = new Child("小军", 2);
        new Thread(child1).start();
        new Thread(child2).start();

    }
}


class Toys {
    private String name;

    public Toys(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
}


class Child extends Thread {

    private int chose;
    static Toys car = new Toys("玩具车");  // 静态保证资源只有一份
    static Toys gun = new Toys("玩具枪");
    public Child(String name, int chose) {
        super(name);
        this.chose = chose;
    }

    @Override
    public void run() {
        try {
            play(this.chose);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void play(int i) throws InterruptedException {
        if (this.chose == 1) {
            synchronized (car) { // 小明抢到了玩具车,然后再去抢小军的玩具枪,互相僵持 --> 死锁
                System.out.println(getName() + "获得了" + car.toString());
                Thread.sleep(3000);
                synchronized (gun) {
                    System.out.println(getName() + "获得了" + gun.toString());
                }
            }
        } else if (this.chose == 2) {
            synchronized (gun) { // 小军抢到了玩具枪,然后再去抢小明的玩具车,互相僵持 --> 死锁
                System.out.println(getName() + "获得了" + gun.toString());
                Thread.sleep(3000);
                synchronized (car) {
                    System.out.println(getName() + "获得了" + car.toString());
                }
            }
        }
    }
}
  • 破坏条件2
package com.thread.demo;

public class Demo19 {

    public static void main(String[] args) {
        Child child1 = new Child("小明", 1);
        Child child2 = new Child("小军", 2);
        new Thread(child1).start();
        new Thread(child2).start();
    }
}

class Toys {
    private String name;

    public Toys(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
}


class Child extends Thread {

    private int chose;
    static Toys car = new Toys("玩具车");  // 静态保证资源只有一份
    static Toys gun = new Toys("玩具枪");
    public Child(String name, int chose) {
        super(name);
        this.chose = chose;
    }

    @Override
    public void run() {
        try {
            play(this.chose);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void play(int i) throws InterruptedException {
        if (this.chose == 1) {
            synchronized (car) { // 小明抢到了玩具车
                System.out.println(getName() + "获得了" + car.toString());
                Thread.sleep(3000);
            }// 破坏条件2,释放玩具车给小军
            synchronized (gun) {
                System.out.println(getName() + "获得了" + gun.toString());
            }
        } else if (this.chose == 2) {
            synchronized (gun) { // 小军抢到了玩具枪
                System.out.println(getName() + "获得了" + gun.toString());
                Thread.sleep(3000);
            }// 破坏条件2,释放玩具枪给小明
            synchronized (car) {
                System.out.println(getName() + "获得了" + car.toString());
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Pointer-faker

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

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

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

打赏作者

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

抵扣说明:

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

余额充值