Java学习笔记day19

线程安全问题

原因

多个线程同时操作同一个数据就会出现线程安全问题

解决思想

多个线程同时只能有一个线程对数据进行操作

解决方案

方案1: 同步代码块
语法: 
	synchronized(锁对象){
		要同步的代码
	}
上锁与开锁: 
	当某一个线程进入synchronized代码块中,其锁对象就会上锁,此时别的线程就无法进入,只能在外面等待开锁
	当一个线程执行完synchronized代码块中的代码,此时锁对象就会开锁
注意: 
	所有的对象都可以作为锁对象
	要保证多个线程的锁对象是同一个对象
方案2: 同步方法
语法: 
	访问权限修饰符 synchronized 返回值类型 方法名(形参列表){
		方法体(要同步的代码)
	}
注意: 
	同步方法的锁对象是调用该方法的对象(同步方法的锁对象就是this)
方案3: 同步静态方法
语法: 
	访问权限修饰符 synchronized static 返回值类型 方法名(形参列表){
		方法体(要同步的代码)
	}
注意: 
	同步静态方法的锁对象是该方法所属类的类对象
类对象: 一个类被JVM加载时会产生一个类对象,该对象包含该类的所有信息,如该类的包名,类名,父类名,实现的接口名,属性数量,属性名,方法数量,方法名等信息
	因为一个类只会被加载一次,所以一个类只有一个类对象

死锁(了解)

目的

了解死锁产生的原因

原因

多个线程相互持有对方所需的锁资源

如何避免

尽量不要在同步中使用同步

线程间通讯

方法: 
	唤醒
		锁对象.notify();
			作用: 随机唤醒一个正在休眠的线程
		锁对象.notifyAll();
			作用: 唤醒所有正在休眠的线程
		注意: 只能唤醒使用该锁对象使其休眠的线程
	休眠
		锁对象.wait();
			作用: 让当前线程进入无限期休眠
		锁对象.wait(timeout);
			作用: 让当前线程有限期休眠,参数为休眠时间,单位毫秒
		锁对象.wait(timeout, nanos);
			作用: 让当前线程有限期休眠,休眠时间毫秒 + 纳秒
			参数1休眠时间,单位毫秒
			参数2休眠时间,单位纳秒
		
		wait()与sleep()的区别: 
			wait(): 
				由Object类提供的普通方法
				只能在同步中使用
				休眠期间会释放持有的锁对象
			sleep(): 
				由Thread类提供的静态方法
				可以在同步中,也可以在同步外使用
				休眠期间不会释放持有的锁对象
注意: 
	1.该方法是由Object类提供的
	2.只能在同步代码块或同步方法中使用
	3.必须使用该同步代码块或同步方法的锁对象调用
	
案例: 
	龟兔百米赛跑
	注意: 
		乌龟每秒跑1米
		兔子每秒跑10米
		总共跑了100米
	兔子跑到80米后睡觉了,乌龟跑完后,被兔子听到,兔子才跑完剩余的20米
	分析: 
		乌龟线程
			任务: 跑100米,跑完通知兔子
			速度: 1秒1米
		兔子线程
			任务: 跑100米,跑到80米进入无限期休眠
			速度: 1秒10米

生产者与消费者模式

目的: 模拟工厂生产与销售的情况
分析: 
	销售人员类
		属性: 
			工厂
		方法: 
			销售
				无限使用工厂对象调用销售方法
	生产人员类
		属性: 
			工厂
		方法: 
			生产
				无限使用工厂对象调用生产方法
	工厂类
		属性: 
			商品数量
			最大商品数量
		方法: 
			生产的方法: 同步方法
				判断商品数量 < 最大商品数量
					true: 继续生产
						商品数量 + 1
						打印xxx生产了一个商品,目前库存xxx个商品
						唤醒销售者线程
					false 
						停止当前生产,等待销售
						打印库存已满,xxx停止生产
			销售的方法: 同步方法
				判断商品数量 > 0
					true: 可以销售
						商品数量 - 1
						打印xxx销售了一个商品,目前库存xxx个商品
						唤醒生产者线程
					false: 无货
						停止销售,等待生产
						打印无货,xxx停止销售
	环境类
		创建一个工厂
		创建几个生产者
		创建几个销售者
		启动生产者与销售者

练习

1.四个窗口各自销售100张票
2.四个窗口共同销售1000张票
3.写一个死锁
4.完成龟兔赛跑
5.完成生产者与消费者
6.使用两个线程
	1个线程打印1~26
	1个线程打印a~z
	要求:打印输出结果为1a2b3c4d5e6f....26z
7.使用两个线程
	1个线程打印1~52
	1个线程打印a~z
	要求:打印输出结果为12a34b56c78d910e1112f....5152z
package test01;

public class MyRunnable implements Runnable{
	private int count = 100;
	
	@Override
	public void run() {
		while(count > 0) {
			count--;
			String name = Thread.currentThread().getName();
			System.out.println(name + "卖了1张票, 还剩余" + count + "张票");
		}
	}

}

package test01;

public class Test {
	public static void main(String[] args) {
		new Thread(new MyRunnable(), "窗口一").start();
		new Thread(new MyRunnable(), "窗口二").start();
		new Thread(new MyRunnable(), "窗口三").start();
		new Thread(new MyRunnable(), "窗口四").start();
	}
}

运行结果

package test02;

public class MyRunnable implements Runnable{
	private int count = 1000;
	private final Object LOCK = new Object();
	
	@Override
	public void run() {
		while(count > 0) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized (LOCK) {
				if(count <= 0) {
					return;
				}
				count--;
				String name = Thread.currentThread().getName();
				System.out.println(name + "卖了1张票, 还剩余" + count + "张票");
			}
		}
	}

}

package test02;

public class Test {
	public static void main(String[] args) {
		MyRunnable runnable = new MyRunnable();
		new Thread(runnable, "窗口一").start();
		new Thread(runnable, "窗口二").start();
		new Thread(runnable, "窗口三").start();
		new Thread(runnable, "窗口四").start();
	}
}

运行结果

package test03;

public class Test {
	public static void main(String[] args) {
		Object lock01 = new Object();
		Object lock02 = new Object();
		
		Thread thread01 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("线程一开始");
				synchronized (lock01) {
					System.out.println("进入线程一锁一");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					synchronized (lock02) {
						System.out.println("进入线程一锁二");
					}					
				}
				System.out.println("线程一结束");
			}
		});
		
		Thread thread02 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("线程二开始");
				synchronized (lock02) {
					System.out.println("进入线程二锁二");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					synchronized (lock01) {
						System.out.println("进入线程二锁一");
					}					
				}
				System.out.println("线程二结束");
			}
		});
		
		thread01.start();
		thread02.start();
	}
}

运行结果

package test04;

public interface LockInterface {
	public static final Object LOCK = new Object();
}

package test04;

public class WGRunnable implements Runnable{

	@Override
	public void run() {
		for(int i = 1; i <= 100; i++) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("乌龟跑了" + i + "米");
		}
		System.out.println("乌龟跑完100米了, 乌龟赢了");
		synchronized (LockInterface.LOCK) {
			LockInterface.LOCK.notify();
		}
	}

}

package test04;

public class TZRunnable implements Runnable{

	@Override
	public void run() {
		for(int i = 10; i <= 100; i+=10) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("兔子跑了" + i + "米");
			
			if(i == 80) {
				System.out.println("兔子睡着了");
				synchronized (LockInterface.LOCK) {
					try {
						LockInterface.LOCK.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
		System.out.println("兔子跑完100米了");
	}

}

package test04;

public class Test {
	public static void main(String[] args) {
		new Thread(new WGRunnable()).start();
		new Thread(new TZRunnable()).start();
	}
}

运行结果


package test05;

public class Factory {
	private int num = 0; //商品数量
	private final int MAX_NUM = 100; //商品最大数量
	
	public synchronized void product() {
		String name = Thread.currentThread().getName();
		if(num < MAX_NUM) {
			num++;
			System.out.println(name + "生产了1件商品, 现有商品" + num +"件");
			this.notifyAll();
		}
		else {
			System.out.println("库房已满, " + name + "停止生产");
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public synchronized void sell() {
		String name = Thread.currentThread().getName();
		if(num > 0) {
			num--;			
			System.out.println(name + "销售了1件商品, 现有商品" + num +"件");
			this.notifyAll();
		}
		else {
			System.out.println("库房无货, " + name + "停止销售");
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

package test05;

import java.util.Random;

public class ProductRunnable implements Runnable{
	private Factory factory;
	
	public ProductRunnable(Factory factory) {
		this.factory = factory;
	}
	
	@Override
	public void run() {
		Random random = new Random();
		while(true) {
			int num = random.nextInt(10) + 1;
			try {
				Thread.sleep(num * 100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			factory.product();
		}
	}

}

package test05;

import java.util.Random;

public class SellRunnable implements Runnable{
	private Factory factory;
	
	public SellRunnable(Factory factory) {
		this.factory = factory;
	}
	
	@Override
	public void run() {
		Random random = new Random();
		while(true) {
			int num = random.nextInt(10) + 1;
			try {
				Thread.sleep(num * 100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			factory.sell();
		}
	}
}

package test05;

public class Test {
	public static void main(String[] args) {
		Factory factory = new Factory();
		ProductRunnable pr = new ProductRunnable(factory);
		SellRunnable sr = new SellRunnable(factory);
		
		new Thread(pr, "生产人员一").start();
		new Thread(pr, "生产人员二").start();
		new Thread(pr, "生产人员三").start();
		
		new Thread(sr, "销售人员一").start();
		new Thread(sr, "销售人员二").start();
		new Thread(sr, "销售人员三").start();
	}
}

运行结果

package test06;

public class Test02 {
	public static void main(String[] args) {
		Object lock = new Object();
		
		//打印数字
		Thread thread01 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				for(int i = 1; i < 27; i++) {
					System.out.print(i);
					synchronized (lock) {
						lock.notify();
						if(i != 26) {
							try {
								lock.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}						
					}
				}				
			}
		});
		
		//打印字母
		Thread thread02 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				for(int i = 97; i < 123; i++) {
					char c = (char)i;
					System.out.print(c);
					synchronized (lock) {
						lock.notify();
						if(i != 122) {
							try {
								lock.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}						
					}
				}				
			}
		});
		
		thread01.start();
		thread02.start();
	}
}

运行结果

package test07;

public class Test02 {
	public static void main(String[] args) {
		Object lock = new Object();
		
		//打印数字
		Thread thread01 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				for(int i = 1; i < 53; i++) {
					System.out.print(i);
					if(i % 2 == 0) {
						synchronized (lock) {
							lock.notify();
							if(i != 52) {
								try {								
									lock.wait();
								} catch (InterruptedException e) {
									e.printStackTrace();
								}
							}							
						}
					}
				}
			}
		});
		
		//打印字母
		Thread thread02 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				for(int i = 97; i < 123; i++) {
					char c = (char)i;
					System.out.print(c);
					synchronized (lock) {
						lock.notify();
						if(i != 122) {
							try {								
								lock.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}							
					}
				}
			}
		});
		
		thread01.start();
		thread02.start();
	}
}

运行结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值