本文主要介绍了线程同步的经典案例以及其代码实现,并讲解了四种线程同步的方式,以及其在经典案例中的应用!
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();
}
}