多线程学习笔记2

本文深入探讨了Java中的多线程安全问题,通过车站卖票实例展示了线程不安全的情况,并介绍了如何使用同步代码块和同步方法来解决这个问题。此外,还讨论了死锁的概念并提供了实现代码。最后,通过生产者消费者问题的代码示例,展示了如何使用锁和条件变量实现线程间的协调。
摘要由CSDN通过智能技术生成

多线程安全问题

  • 多线程访问了共享数据,会产生安全问题
  • 线程不安全的例子如下,最经典的车站卖票实例
//自定义线程类
public class MyRunnable implements Runnable {
    private int ticket = 100;
    @Override
    public void run() {
        try {
            //睡眠10毫秒,提高线程安全的概率
            Thread.sleep(10);
            while (true){
                if(ticket>0){
                    System.out.println("当前线程"+Thread.currentThread().getName()+"正在买第"+ticket+"张票");
                    ticket--;
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//测试类
public class MyTest {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr);
        t1.start();
        Thread t2 = new Thread(mr);
        t2.start();
        Thread t3 = new Thread(mr);
        t3.start();
    }
}

以上代码运行结果截图如下:

线程同步来解决线程安全问题:

  • 同步代码块
  • 同步方法
  • 锁机制

同步代码块

格式:

synchronized(锁对象){

         可能会出现线程安全的代码(访问了共享数据的代码)
}

注意:

通过代码中的锁对象,可以使用任意的对象

但是必须保证多个线程使用的锁对象是同一个

锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行

public class MyRunnable implements Runnable {
    private int ticket = 100;
    Object obj = new Object();
    @Override
    public void run() {
       synchronized(obj){
           try {
               //睡眠10毫秒,提高线程安全的概率
               Thread.sleep(1000);
               while (true){
                   if(ticket>0){
                       System.out.println("当前线程"+Thread.currentThread().getName()+"正在买第"+ticket+"张票");
                       ticket--;
                   }
               }
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
    }
}


//测试类
public class MyTest {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr);
        t1.start();
        Thread t2 = new Thread(mr);
        t2.start();
        Thread t3 = new Thread(mr);
        t3.start();
    }
}

同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁进不去同步

同步方法

使用步骤:

  • 把共享数据的数据抽取出来放在一个方法里,
  • 在方法上添加synchronized修饰符

格式:

修饰符 synchronized 返回值类型 方法名(参数列表){

        可能会出现线程安全的问题的代码(访问了共享数据的代码)

}

public class MyRunnable2 implements Runnable {
    private int ticket = 100;
    Object obj = new Object();
    @Override
    public void run() {
       while (true){
           payTicket();
       }
    }

    /*
        同步方法也会把内部的代码锁住,只让一个线程执行,同步方法的锁对象是 new MyRunnable2()
        也就是this
      */
    public synchronized void payTicket(){
        try {
            //睡眠10毫秒,提高线程安全的概率
            Thread.sleep(1000);
            if(ticket>0){
                System.out.println("当前线程"+Thread.currentThread().getName()+"正在买第"+ticket+"张票");
                ticket--;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}



//測試代碼
public class MyTest {
    public static void main(String[] args) {
        MyRunnable2 mr = new MyRunnable2();
        Thread t1 = new Thread(mr);
        t1.start();
        Thread t2 = new Thread(mr);
        t2.start();
        Thread t3 = new Thread(mr);
        t3.start();
    }
}

死锁的实现代码:

public class MyTest {
    public static void main(String[] args) {
        Object objA = new Object();
        Object objB = new Object();

        new Thread(()->{
            while(true){
                synchronized (objA){
//                    线程一
                    synchronized (objB){
                        System.out.println("张三正在走路。。。。");
                    }
                }
            }
        }).start();
        new Thread(()->{
            while (true){
                synchronized (objB){
//                    线程二
                    synchronized (objA){
                        System.out.println("李四正在走路。。。。");
                    }
                }
            }
        }).start();
    }
}

生产者和消费者

需要执行的步骤如图:

代码实现如下:

//标记类 辅助作用
public class Desk {
    /**
        桌子上是否有面包
     */
    public static boolean FLAG = false;
    /**
     * 汉堡的总数
     */
    public static int count = 10;

    /**
     * 锁对象
     */
    public static final Object lock = new Object();

}


/**
 * @author Joshua
 * @since 2021/8/11
 * 消费者步骤:
 *     1、判断桌子上是否有汉堡
 *     2、如果没有就等待
 *     3、如果有就开吃
 *     4、吃完之后,桌子上的汉堡就没有了,叫醒等待的生产者继续生产,汉堡的总数量减1
 */
public class Foodie extends Thread{
    @Override
    public void run() {
        while(true){
            synchronized (Desk.lock){
                //如果吃货已经吃了10个,就直接退出
                if(Desk.count<=0){
                    break;
                }else{
                    //判断桌子上是否有汉堡
                    if(Desk.FLAG){
                        //如果有就开吃
                        System.out.println("吃货开始吃汉堡。。。。。真好吃");
                        Desk.FLAG = false;
                        Desk.lock.notifyAll();
                        Desk.count--;
                    }else{
                        //如果没有就等待
                        //使用什么对象当作锁,就使用什么对象去调用等待和唤醒的方法
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}



/**
 * @author joshua
 * @since 2021/8/11
 * 生成者步骤:
 *     1、判断桌子上是否有汉堡
 *     2、有就等待,没有就生产
 *     3、把汉堡放在桌子上
 *     4、叫醒等待的消费者开吃
 */
public class Cooker extends Thread {
    @Override
    public void run() {
        synchronized (Desk.lock){
            while (true){
                if(Desk.count<=0){
                    break;
                }else{
                    //判断桌子上是否有汉堡
                    if(Desk.FLAG){
                        //有就等待
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else{
                        //没有就生产
                        System.out.println("大厨正在生产汉堡.....完工。");
                        //把汉堡放在桌子上
                        Desk.FLAG=true;
                        //叫醒等待的消费者开吃
                        Desk.lock.notifyAll();
                    }
                }
            }
        }

    }
}


//测试类
public class MyTest {
  
    public static void main(String[] args) {
        Foodie foodie = new Foodie();
        foodie.start();


        Cooker cooker = new Cooker();
        cooker.start();
    }
}















上面代码的Desk类并不符合面向对象,下面进行代码改写

public class Desk {
    /**
        桌子上是否有面包
     */
    public boolean flag;
    /**
     * 汉堡的总数
     */
    public int count ;

    /**
     * 锁对象
     */
    public final Object lock = new Object();

    public Desk() {
        //初始化 flag和count的值
        this(false,10);
    }

    public Desk(boolean flag, int count) {
        this.flag = flag;
        this.count = count;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public Object getLock() {
        return lock;
    }
}



/**
 * @author Joshua
 * @since 2021/8/11
 * 消费者步骤:
 *     1、判断桌子上是否有汉堡
 *     2、如果没有就等待
 *     3、如果有就开吃
 *     4、吃完之后,桌子上的汉堡就没有了,叫醒等待的生产者继续生产,汉堡的总数量减1
 */
public class Foodie extends Thread{
    private Desk desk;
    public Foodie(Desk desk){
        this.desk = desk;
    }

    @Override
    public void run() {
        while(true){
            synchronized (desk.getLock()){
                //如果吃货已经吃了10个,就直接退出
                if(desk.getCount()<=0){
                    break;
                }else{
                    //判断桌子上是否有汉堡
                    if(desk.isFlag()){
                        //如果有就开吃
                        System.out.println("吃货开始吃汉堡。。。。。真好吃");
                        desk.setFlag(false);
                        desk.getLock().notifyAll();
                        desk.setCount(desk.getCount()-1);
                    }else{
                        //如果没有就等待
                        //使用什么对象当作锁,就使用什么对象去调用等待和唤醒的方法
                        try {
                            desk.getLock().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}



/**
 * @author joshua
 * @since 2021/8/11
 * 生成者步骤:
 *     1、判断桌子上是否有汉堡
 *     2、有就等待,没有就生产
 *     3、把汉堡放在桌子上
 *     4、叫醒等待的消费者开吃
 */
public class Cooker extends Thread {
    private Desk desk;
    public Cooker(Desk desk){
        this.desk = desk;
    }
    @Override
    public void run() {
        synchronized (desk.getLock()){
            while (true){
                if(desk.getCount()<=0){
                    break;
                }else{
                    //判断桌子上是否有汉堡
                    if(desk.isFlag()){
                        //有就等待
                        try {
                            desk.getLock().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else{
                        //没有就生产
                        System.out.println("大厨正在生产汉堡.....完工。");
                        //把汉堡放在桌子上
                        desk.setFlag(true);
                        //叫醒等待的消费者开吃
                        desk.getLock().notifyAll();
                    }
                }
            }
        }

    }
}




public class MyTest {
    /*
    消费者步骤:
    1、判断桌子上是否有汉堡
    2、如果没有就等待
    3、如果有就开吃
    4、吃完之后,桌子上的汉堡就没有了,叫醒等待的生产者继续生产,汉堡的总数量减1

    生成者步骤:
    1、判断桌子上是否有汉堡
    2、有就等待,没有就生产
    3、把汉堡放在桌子上
    4、叫醒等待的消费者开吃
     */

    public static void main(String[] args) {
        Desk desk = new Desk();

        Foodie foodie = new Foodie(desk);
        foodie.start();


        Cooker cooker = new Cooker(desk);
        cooker.start();
    }
}









使用队列实现生产者消费者的案例

//生产者类
public class Cooker extends Thread {
    private ArrayBlockingQueue<String> list;
    public Cooker(ArrayBlockingQueue<String> list) {
        this.list = list;
    }

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(100);
                list.put("食物");
                System.out.println("厨师向队列中放入食物");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

//消费者类
public class Foodie extends Thread {
    private  ArrayBlockingQueue<String> list;
    public Foodie(ArrayBlockingQueue<String> list) {
        this.list = list;
    }

    @Override
    public void run() {

        while (true){
            try {
                Thread.sleep(100);
                
                System.out.println("吃货从队列中取走"+list.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


//测试类

public class MyTest {
    public static void main(String[] args) {
        //创建一个队列 默认容量为1
        ArrayBlockingQueue<String> list = new ArrayBlockingQueue<>(1);
        Foodie foodie = new Foodie(list);
        foodie.start();
        Cooker cooker = new Cooker(list);
        cooker.start();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值