• 线程同步—— 生产者与消费者、龟兔赛跑、双线程打印


    本文主要介绍了线程同步的经典案例以及其代码实现,并讲解了四种线程同步的方式,以及其在经典案例中的应用!

    1.多个线程共享同一个对象锁的四种实现方式

    方案一:

    • 创建一个线程任务,传给四个线程对象(线程任务对象只有一个,对象的成员变量(对象锁)也只有一个)

    方案二:

    • 使用static修饰需要共享的成员变量(此静态变量(对象锁)属于该类创建的所有对象)

    方案三:

    • 创建一个工具类,里面含有静态成员变量(使用类名.变量名(对象锁)调用)

    双线程打印是使用此种方式实现的


    方案四:

    • 使用构造函数(类似于接口回调:为线程任务提供一个有参构造函数,在创建线程任务时传入,可以保证多个线程任务操作同一个传进的外部对象(对象锁)

    龟兔赛跑、生产者与消费者是使用此种方式实现的

    2.生产者与消费者


    • 使用同步方法实现线程同步,创建线程任务时,使用构造函数(类似于接口回调:为线程任务提供一个有参构造函数,在创建线程任务时传入,可以保证多个线程任务操作同一个传进的外部对象(对象锁)
    • 代码注释非常详细
      在这里插入图片描述

    (1)工厂(含有同步方法,也是下文线程同步锁对象)

    public class Factory {
    	//库房最大数量
    	private final int maxNum=10;
    	//库房当前数量
    	private int currentNum=0;
    	
    	//工厂生产方法
    	public synchronized void get() {
    		
    		//生产商品的时间(阻塞一会)
    		try {
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    		//获取生产者信息
    		String name=Thread.currentThread().getName();
    		//库房未满
    		if (currentNum<maxNum) {
    			//商品+1
    			currentNum++;
    			//输出生产信息
    			System.out.println(name+"生产了一件商品,库房当前商品数量为:"+currentNum);
    			//唤醒所有消费者去卖,自己接着生产
    			this.notifyAll();
    		}
    		else {	//	库房已满
    			System.out.println(name+"停止生产(库房已满)");
    			//陷入无限期休眠,等待消费者唤醒
    			try {
    				this.wait();	//	线程进入休眠,不会占用锁资源(锁对象)
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	//工厂消费方法
    	public synchronized void put() {
    		
    		//消费商品的时间(阻塞一会)
    		try {
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    		//获取消费者信息
    		String name=Thread.currentThread().getName();
    		//库房是否有商品
    		if (currentNum>0) {
    			currentNum--;
    			//输出消费信息
    			System.out.println(name+"销售了一件商品,库房当前商品数量为:"+currentNum);
    			//唤醒消费者去生产商品
    			this.notifyAll();
    		}
    		else {	//	库房已经空了
    			//输出信息
    			System.out.println(name+"停止销售(库房已空)");
    			//消费者进入无限期休眠,等待生产者唤醒
    			try {
    				this.wait();//线程休眠,不会占用锁资源
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    (2)生产者

    public class ProductionRunnable implements Runnable{
    
    	//保证在同一个工厂(同一个锁对象)
    	private Factory factory;
    	
    	//构造函数
    	public ProductionRunnable(Factory factory) {
    		// TODO Auto-generated constructor stub
    		this.factory=factory;	//保证在同一个工厂(同一个锁对象)
    	}
    	
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		//表示一直生产生产
    		while(true) {
    			this.factory.get();
    		}
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    (3)消费者

    public class SaleRunnable implements Runnable {
    	//保证同一个锁对象(同一个工厂)
    	private Factory factory;
    	
    	//构造函数
    	public SaleRunnable(Factory factory) {
    		// TODO Auto-generated constructor stub
    		this.factory=factory;
    	}
    
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		//表示此线程一直销售
    		while(true) {
    			this.factory.put();
    		}
    		
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    (4)测试环境

    public class Test {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		//创建工厂(只有一个,保证同一个锁)
    		Factory factory=new Factory();
    
    		//创建工人
    		Thread thread01=new Thread(new SaleRunnable(factory),"小芳");
    		Thread thread02=new Thread(new ProductionRunnable(factory),"老李");
    		Thread thread03=new Thread(new ProductionRunnable(factory),"小张");
    		
    		//启动线程,工人工作
    		thread01.start();
    		thread02.start();
    		thread03.start();		
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3.龟兔赛跑


    在这里插入图片描述

    (1)跑步类(含有同步方法,也是下文线程同步锁对象)

    public class Race {
    	
    	//兔子跑步
    	public synchronized void rabbitRun() {
    		
    		//获取到线程名称
    		String name = Thread.currentThread().getName();
    		
    		for(int i=1;i<=100;i++) {
    			//消耗10ms
    			try {
    				this.wait(10);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			//打印结果
    			if (i==81) {
    				//兔子进入沉睡
    				try {
    					System.out.println(name+"睡着了!");
    					this.wait();
    				} catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    				System.out.println(name+"醒来了");
    			}
    			
    			System.out.println(name+"跑了:"+i+"米");
    			
    		}
    		System.out.println(name+"跑完了!");
    	}
    	
    	//乌龟跑步
    	public synchronized void tortoiseRun() {
    		//获取到线程名称
    		String name = Thread.currentThread().getName();
    		
    		for(int i=1;i<=100;i++) {
    			//消耗10ms
    			try {
    				this.wait(100);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println(name+"跑了:"+i+"米");
    		}
    		
    		//跑到了终点,唤醒兔子
    		System.out.println(name+"跑完了!");
    		this.notifyAll();//唤醒兔子
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    (2)兔子线程任务

    public class RabbitRunnable implements Runnable {
    	//定义Race变量
    	private Race race;
    	
    	//构造函数接收锁对象
    	public RabbitRunnable(Race race) {
    		this.race=race;
    	}
    
    	//调用同步方法,锁对象时调用同步方法的对象
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		this.race.rabbitRun();
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    (3)乌龟线程任务

    public class TortoiseRunnable implements Runnable {
    	//定义Race对象
    	private Race race;
    	
    	//接收锁对象
    	public TortoiseRunnable(Race race) {
    		this.race=race;
    	}
    
    	//开始跑步
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		this.race.tortoiseRun();
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    (4)测试环境

    public class Test {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		//创建跑步
    		Race race=new Race();
    		
    		//创建任务对象
    		RabbitRunnable runnable01=new RabbitRunnable(race);
    		TortoiseRunnable runnable02=new TortoiseRunnable(race);
    		
    		//创建线程
    		Thread thread01=new Thread(runnable01, "兔子");
    		Thread thread02=new Thread(runnable02, "乌龟");
    		
    		//启动线程
    		thread01.start();
    		thread02.start();
    		
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    4.双线程打印


    任务: 打印a12b45c56…(创建两个线程分别实现字母和数字的打印)
    核心思想: 创建一个工具类,里面包含一个 static final 修饰的对象充当锁,进而实现字母和数字的连贯打印

    (1)创建静态锁对象

    public class Lock {
    	//声明一个锁对象
    	public static final Object LOCK=new Object();
    	
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (2)字母打印线程任务

    public class CharRunnable implements Runnable {
    
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		synchronized (Lock.LOCK) {
    			for(int i=97;i<=122;i++) {
    				//唤醒数字进程
    				Lock.LOCK.notifyAll();
    				//打印字符
    				System.out.println((char)i);
    				//进入睡眠
    				try {
    					Lock.LOCK.wait();	//当前正在使用此锁的线程进入休眠
    				} catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    			//最后的字母打印完成后,唤醒数字线程
    			Lock.LOCK.notifyAll();
    		}
    	}
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    (3)数字打印线程任务

    public class NumRunnable implements Runnable {
    
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		synchronized (Lock.LOCK) {
    			for(int i=1;i<=52;i++) {
    				//唤醒字母打印线程
    				Lock.LOCK.notifyAll();
    				//打印数字
    				System.out.println(i++);
    				System.out.println(i);
    				//数字打印进入休眠
    				try {
    					Lock.LOCK.wait();
    				} catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    			
    		}
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    (4)测试环境

    public class Test {
    	
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		Thread thread01=new Thread(new CharRunnable());
    		Thread thread02=new Thread(new NumRunnable());
    		
    		thread01.start();
    		thread02.start();
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    实用篇-ES环境搭建
    Torch学习(一)
    彩色相机工作原理——bayer格式理解
    车载相关名词--车载数据中心方案
    Linux 访问系统日志文件
    基于Spring Boot的网上购物商城系统
    安装kibana
    docker for windonws--Windows 10 家庭中文版安装clickhouse 22.3版本及配置
    深入浅出 C++ 11 右值引用
    bat脚本守护进程
  • 原文地址:https://blog.csdn.net/weixin_43715360/article/details/125667305