Day29-同步方法及同步块、死锁和锁、线程协作、线程池

Day29-同步方法及同步块、死锁和锁、线程协作、线程池

同步方法及同步块

同步方法

在这里插入图片描述

同步方法弊端

  • 方法里面需要修改的内容才需要锁,锁得太多会浪费资源。

同步块

在这里插入图片描述

三大不安全案例的改进

  • 买票(加上synchronized 同步方法)
package com.ghy.syn;
//安全的买票
public class SafeBuyTicket {
    public static void main(String[] args) {
        BuyTickets buyTickets =new BuyTickets();
        new Thread(buyTickets,"赵学生").start();
        new Thread(buyTickets,"赵老师").start();
        new Thread(buyTickets,"赵黄牛").start();
    }
}

//买票的方法
class BuyTickets implements Runnable{
    //10张票
    private int ticketNums =10;
    boolean flag =true;//外部停止方式

    @Override
    public void run() {
        //买票
        while (flag){
            buy();
        }
    }
    //synchronized 同步方法,锁的是this
    private synchronized void buy(){
        //判断是否有票
        if(ticketNums<=0){
            flag=false;//循环停止
            return;
        }
        //模拟延时
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //买票
        System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票!");
    }
}

输出

赵学生拿到了第10张票!
赵学生拿到了第9张票!
赵学生拿到了第8张票!
赵学生拿到了第7张票!
赵学生拿到了第6张票!
赵学生拿到了第5张票!
赵学生拿到了第4张票!
赵黄牛拿到了第3张票!
赵黄牛拿到了第2张票!
赵黄牛拿到了第1张票!
  • 银行取钱(对账户进行监控上锁,用同步块)
package com.ghy.syn;
//安全的取钱
//两个人去取钱,要有账户
public class SafeBank {
    public static void main(String[] args) {
        //账户
        SafeAccount safeAccount =new SafeAccount(100,"结婚基金");
        SafeDrawing xiaoZhao =new SafeDrawing(safeAccount,50,"小赵");
        SafeDrawing xiaoGong =new SafeDrawing(safeAccount,80,"小龚");
        //线程启动
        xiaoZhao.start();
        xiaoGong.start();
    }
}

//账户
class SafeAccount{
    int money;//余额
    String name;//卡名

    public SafeAccount(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//银行:模拟取钱
class SafeDrawing extends Thread{
    SafeAccount safeAccount;//账户
    int drawingMoney;//取了多少钱
    int nowMoney;//现在多少钱
    public SafeDrawing (SafeAccount safeAccount,int drawingMoney,String name){
        super(name);
        this.safeAccount=safeAccount;
        this.drawingMoney=drawingMoney;
    }
    //取钱

    @Override
    public void run() {
        synchronized (safeAccount){
            //判断余额是否有钱取出
            if(safeAccount.money-drawingMoney<0){
                System.out.println(Thread.currentThread().getName()+"你个穷瓜蛋没有钱!");
                return;
            }
            //模拟延时
            //sleep可以方法问题的发生性
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //卡内余额=余额-你取出来的钱
            safeAccount.money = safeAccount.money - drawingMoney;
            //你手里现在的钱=现在的钱 + 你取出来的钱
            nowMoney = nowMoney + drawingMoney;
            System.out.println(safeAccount.name+"余额为"+safeAccount.money+"万");
            System.out.println(Thread.currentThread().getName()+"手里的钱"+nowMoney+"万");

        }
    }
}

输出

结婚基金余额为50万
小赵手里的钱50万
小龚你个穷瓜蛋没有钱!

要点:

锁的对象就是变化的量(账户),需要增删改的对象。

  • 集合大小
package com.ghy.syn;
import java.util.ArrayList;
import java.util.List;
//线程安全的集合
public class SafeList {
    public static void main(String[] args) {
        List<String> list =new  ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        //模拟延时
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //输出线程大小
        System.out.println(list.size());
    }
}

输出

10000

CopyOnWriteArrayList(保证线程集合安全)

package com.ghy.syn;

import java.util.concurrent.CopyOnWriteArrayList;

//测试JUC安全类型的集合
public class TestJUC {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        //模拟延时
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

输出

10000

死锁

在这里插入图片描述

  • 产生死锁
package com.ghy.demo05;
//死锁:多个线程互相抱着对方需要的资源,形成僵持
public class DeadLock {
    public static void main(String[] args) {
        Makeup p1 = new Makeup(0,"小赵");
        Makeup p2 = new Makeup(1,"小龚");

        //启动线程
        p1.start();
        p2.start();
    }
}
//口红
class Lipstick{}


//镜子
class Mirror{

}
//化妆
class Makeup extends Thread {
    //需要的资源只有一份,用static保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    int choice;//选择
    String name;//使用化妆品的人

    Makeup(int choice, String name) {
        this.choice = choice;
        this.name = name;
    }

    @Override
    public void run() {
        //化妆
        try {
            makeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //化妆,互相持有对方的锁,就是需要拿到对方的资源
    private void makeUp() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipstick) {//获得口红的锁
                System.out.println(this.name + "拿到口红化妆啦!");
                Thread.sleep(1000);
                synchronized (mirror) {//一秒钟后想获得镜子
                    System.out.println(this.name + "拿到镜子化妆啦!");
                }
            }
        } else {
            synchronized (mirror) {//获得镜子的锁
                System.out.println(this.name + "拿到镜子化妆啦!");
                Thread.sleep(2000);
                synchronized (lipstick) {//两秒钟后想获得镜子
                    System.out.println(this.name + "拿到口红化妆啦!");
                }
            }
        }
    }
}

输出

在这里插入图片描述

产生了死锁,程序一直在这里僵持着

  • 解决死锁(把锁拿出来,不要拿对方的锁)
package com.ghy.demo05;
//死锁:多个线程互相抱着对方需要的资源,形成僵持
public class DeadLock {
    public static void main(String[] args) {
        Makeup p1 = new Makeup(0,"小赵");
        Makeup p2 = new Makeup(1,"小龚");

        //启动线程
        p1.start();
        p2.start();
    }
}
//口红
class Lipstick{}


//镜子
class Mirror{

}
//化妆
class Makeup extends Thread {
    //需要的资源只有一份,用static保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    int choice;//选择
    String name;//使用化妆品的人

    Makeup(int choice, String name) {
        this.choice = choice;
        this.name = name;
    }

    @Override
    public void run() {
        //化妆
        try {
            makeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //化妆,互相持有对方的锁,就是需要拿到对方的资源
    private void makeUp() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipstick) {//获得口红的锁
                System.out.println(this.name + "拿到口红化妆啦!");
                Thread.sleep(1000);
                }
            synchronized (mirror) {//一秒钟后想获得镜子
                System.out.println(this.name + "拿到镜子化妆啦!");
            }
        } else {
            synchronized (mirror) {//获得镜子的锁
                System.out.println(this.name + "拿到镜子化妆啦!");
                Thread.sleep(2000);
                }
            synchronized (lipstick) {//两秒钟后想获得镜子
                System.out.println(this.name + "拿到口红化妆啦!");
            }
        }
    }
}

输出

小赵拿到口红化妆啦!
小龚拿到镜子化妆啦!
小赵拿到镜子化妆啦!
小龚拿到口红化妆啦!

死锁避免方法

在这里插入图片描述

在这里插入图片描述

package com.ghy.gaoji;
import java.util.concurrent.locks.ReentrantLock;
//测试Lock锁
public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 =new TestLock2();

        new Thread(testLock2,"小赵").start();
        new Thread(testLock2,"小龚").start();
        new Thread(testLock2,"小小赵").start();

    }
}
class TestLock2 implements Runnable{
    int ticketNums = 10;
    //定义Lock锁
    private final ReentrantLock lock =new ReentrantLock();
    @Override
    public void run() {
        while(true){

            try{
                lock.lock();//加锁
                if(ticketNums>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
                }else{
                    break;
                }
            }finally {
                //解锁
                lock.unlock();
            }

        }
    }
}

输出

小赵拿到了第10张票
小赵拿到了第9张票
小赵拿到了第8张票
小赵拿到了第7张票
小赵拿到了第6张票
小赵拿到了第5张票
小赵拿到了第4张票
小赵拿到了第3张票
小赵拿到了第2张票
小赵拿到了第1张票

在这里插入图片描述

synchronized和Lock的对比

在这里插入图片描述

(ReentrantLock:可重入锁)

线程协作

线程通信

在这里插入图片描述

分析

在这里插入图片描述

线程通信方法

在这里插入图片描述

两种解决方法

  1. 管程法:建立缓冲区,生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
  2. 信号灯:通过判断方式,让线程什么时候等待什么时候唤醒。

管程法

package com.ghy.gaoji;
//管程法
//测试:生产者消费者模型-->利用缓冲区解决
//生产者、消费者、产品、缓冲区
public class TestPC {
    public static void main(String[] args) {
        //容器
        SynContainer container =new SynContainer();
        //生产者
        new Productor(container).start();
        //消费者
        new Consumer(container).start();
    }
}

//生产者
class Productor extends Thread{
    SynContainer container;
    public Productor(SynContainer container){
        this.container = container;
    }
    //生产

    @Override
    public void run() {
        for (int i = 0; i <=20; i++) {
            container.push(new Good(i));
            System.out.println("生产了编号为"+i+"的小赵");
        }
    }
}
//消费者
class Consumer extends  Thread{
    SynContainer container;
    public Consumer(SynContainer container){
        this.container = container;
    }
    //消费

    @Override
    public void run() {
        for (int i = 0; i <=20; i++) {
            System.out.println("消费了编号为"+container.pop().id+"的小赵");
        }
    }
}
//产品
class Good{
    int id;//产品编号

    public Good(int id) {
        this.id = id;
    }
}
//缓冲区
class SynContainer{
    //需要一个容器大小
    Good [] goods =new Good[10];
    //容器计数器
    int count = 0;

    //生产者放入产品
    public synchronized void push(Good good){
        //如果容器满了,就需要等待消费
        if(count==goods.length){
            //通知消费者消费,生产者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //如果容器没有满,我们就需要丢入产品
        goods[count] = good;//放入
        count++;
        //可以通知消费者消费了
        this.notifyAll();
    }

    //消费者消费产品
    public synchronized  Good pop(){
        //判断是否能消费
        if(count==0){
            //等待生产者生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果可以消费
        count--;
        Good good = goods[count];//取出
        //吃完了,通知生产者生产
        this.notifyAll();//通知
        return good;
    }
}

输出

生产了编号为0的小赵
生产了编号为1的小赵
生产了编号为2的小赵
生产了编号为3的小赵
生产了编号为4的小赵
生产了编号为5的小赵
生产了编号为6的小赵
生产了编号为7的小赵
生产了编号为8的小赵
生产了编号为9的小赵
消费了编号为9的小赵
消费了编号为10的小赵
生产了编号为10的小赵
生产了编号为11的小赵
消费了编号为8的小赵
消费了编号为12的小赵
消费了编号为11的小赵
消费了编号为7的小赵
消费了编号为6的小赵
消费了编号为5的小赵
消费了编号为4的小赵
消费了编号为3的小赵
消费了编号为2的小赵
消费了编号为1的小赵
生产了编号为12的小赵
消费了编号为0的小赵
生产了编号为13的小赵
生产了编号为14的小赵
消费了编号为13的小赵
消费了编号为15的小赵
消费了编号为14的小赵
生产了编号为15的小赵
生产了编号为16的小赵
生产了编号为17的小赵
生产了编号为18的小赵
生产了编号为19的小赵
生产了编号为20的小赵
消费了编号为17的小赵
消费了编号为20的小赵
消费了编号为19的小赵
消费了编号为18的小赵
消费了编号为16的小赵

信号灯法

package com.ghy.gaoji;
//测试生产者消费者问题2:信号灯法
public class TestPC2 {
    public static void main(String[] args) {
        Tv tv =new Tv();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}
//生产者-->演员
class Player extends Thread{
    Tv tv;
    public Player(Tv tv){
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i <=10; i++) {
            if(i%2==0){
                this.tv.play("节目:<小赵蹦叉叉>");
            }else{
                this.tv.play("广告:<小赵打广告>");
            }
        }
    }
}

//消费者-->观众
class Watcher extends Thread{
    Tv tv;
    public Watcher(Tv tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i <=10; i++) {
            tv.watch();
        }
    }
}

//产品-->节目
class Tv{
    //演员表演,观众等待   T
    //观众观看,演员等待   F
    String voice;//节目
    boolean flag =true;//标志位

    //演员表演
    public  synchronized void play(String voice){
        //如果观众在观看,演员就等待
        if(!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("小赵表演了:"+voice);
        //通知观众观看
        this.notifyAll();//通知唤醒
        this.voice=voice;
        this.flag=!this.flag;//标志位切换
    }

    //观众观看
    public synchronized  void watch(){
        //如果演员在表演,观众就等待
        if(flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("小龚观看了:"+voice);
        //观看了后通知演员表演
        this.notifyAll();
        this.flag=!this.flag;//标志位切换
    }
}

输出

小赵表演了:节目:<小赵蹦叉叉>
小龚观看了:节目:<小赵蹦叉叉>
小赵表演了:广告:<小赵打广告>
小龚观看了:广告:<小赵打广告>
小赵表演了:节目:<小赵蹦叉叉>
小龚观看了:节目:<小赵蹦叉叉>
小赵表演了:广告:<小赵打广告>
小龚观看了:广告:<小赵打广告>
小赵表演了:节目:<小赵蹦叉叉>
小龚观看了:节目:<小赵蹦叉叉>
小赵表演了:广告:<小赵打广告>
小龚观看了:广告:<小赵打广告>
小赵表演了:节目:<小赵蹦叉叉>
小龚观看了:节目:<小赵蹦叉叉>
小赵表演了:广告:<小赵打广告>
小龚观看了:广告:<小赵打广告>
小赵表演了:节目:<小赵蹦叉叉>
小龚观看了:节目:<小赵蹦叉叉>
小赵表演了:广告:<小赵打广告>
小龚观看了:广告:<小赵打广告>
小赵表演了:节目:<小赵蹦叉叉>
小龚观看了:节目:<小赵蹦叉叉>

线程池

使用线程池

在这里插入图片描述

在这里插入图片描述

package com.ghy.gaoji;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//测试线程池
public class TestPool {
    public static void main(String[] args) {
        //1.创建服务,创建线程池
        //newFixedThreadPool 参数为线程池的大小
        ExecutorService service = Executors.newFixedThreadPool(10);

        //执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        //2.关闭连接
        service.shutdown();
    }

}
class MyThread implements Runnable{
    @Override
    public void run() {
            System.out.println(Thread.currentThread().getName());
    }
}

输出

pool-1-thread-1
pool-1-thread-4
pool-1-thread-2
pool-1-thread-3

总结

package com.ghy.gaoji;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

//总结线程的创建
public class All {
    public static void main(String[] args) {
        new Thread1().start();
        new Thread(new Thread2()).start();

        FutureTask<Integer> futureTask =new FutureTask<Integer>(new Thread3());
        new Thread(futureTask).start();

        try {
            Integer integer =futureTask.get();
            System.out.println(integer);//打印返回值
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
//1.继承Thread类
class Thread1 extends Thread{
    @Override
    public void run() {
        System.out.println("继承Thread类");
    }
}
//2.实现Runnable接口
class Thread2 implements Runnable{
    @Override
    public void run() {
        System.out.println("实现Runnable接口");
    }
}
//3.实现Callable
class Thread3 implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("实现Callable");
        return 100;
    }
}

输出

继承Thread类
实现Runnable接口
实现Callable
100
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值