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

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

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();
			}
		}
	}
}

(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();
		}
	}

}

(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();
		}
		
	}
}

(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();		
	}
}

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();//唤醒兔子
	}
}

(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();
	}
}

(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();
	}
}

(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();
		
	}

}

4.双线程打印


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

(1)创建静态锁对象

public class Lock {
	//声明一个锁对象
	public static final Object LOCK=new Object();
	
}

(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();
		}
	}
	
}

(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();
				}
			}
			
		}
	}

}

(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();
	}

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值