Java复习之知识点整理(八)----多线程的生产消费关系,死锁问题,Runnable

Part1、生产消费关系
-------------------------------------
1.容器加上限:库存超过一定数量,不再生产。
2.生产消费互斥:同一时间只能有一个人访问库存。

Part2、等待 -- 通知模式 (wait --- notify)
-------------------------------------
1.wait():让当前线程等待,将线程加入到锁对象等待区,同时释放锁
2.notify():唤醒线程,在等待区随机唤醒一个线程,注意是随机的,但是不会释放锁,不会操作锁
3.sleep():仅仅是放弃CPU的抢占权,与锁无关
4.wait和notify虽然是object的方法,但是必须在线程,在synchonized中才能起作用
5.当等待区线程被唤醒,会立刻执行wait之后的代码
6.wait 和 notify 方法,是当前锁对象的方法,必须是当前锁对象调用,注意一致性

Part3、死锁
-------------------------------------------
1.一个锁的时候,所有的线程都进入了等待队列,死锁
2.多个锁的时候,无锁可用,死锁
3.解决死锁方法1:wait(int n),最多等待n毫秒,期间可以被唤醒

4.解决死锁方法2:notifyAll()方法,通知等待队列中的所有等待线程,可以去抢锁抢CPU了


Part1,2,3 代码示例:

public class Ts01 {

	public static void main(String[] args) {

		MyList list = new MyList();

		Product p = new Product(list);
		Product p1 = new Product(list);
		Product p2 = new Product(list);
		Customer c = new Customer(list);
		p.start();
		p1.start();
		p2.start();
		c.start();
	}
}

class MyList {

	private List<Integer> list = new ArrayList<Integer>();

	private int MAX = 1;

	// 添加
	public synchronized void add(Integer i) {

		//如果仓库已满,就释放锁,并且进入等待区
		//如果被唤醒,立刻执行wait之后代码,即进入循环继续判断仓库是否已经满
		while (list.size() >= MAX) {
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		//仓库未满,开始生产
		list.add(i);
		System.out.println("库存" + list.size());
		//生产完毕,通知等待区,随机唤醒一个线程(如果唤醒的是生产线程,那么继续唤醒)
		//直到唤醒消费线程
		notify();
		
	}

	// 取出
	public synchronized Integer remove() {

		//如果仓库为空,则进入等待区等待
		//如果被唤醒,接着wait执行,继续判断仓库是否为空
		while(list.isEmpty())
		{
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		//仓库不为空,开始消费
		Integer i = list.remove(0);
		//消费完毕,通知,唤醒等待区的随机线程。如果唤醒的是消费线程,那么继续唤醒
		//直到唤醒生产线程
		notify();
		return i;
	}

	// 判空
	public boolean isEmpty() {
		return list.isEmpty();
	}

}

// 生产者
class Product extends Thread {
	private MyList list = null;

	static int i = 1;
	public Product(MyList list) {
		this.list = list;
	}

	public void run() {
		
		while (true) {
			list.add(i);
			System.out.println("增加了" + i);
			i++;
		}
	}
}

// 消费者
class Customer extends Thread {
	private MyList list = null;

	public Customer(MyList list) {
		this.list = list;
	}

	public void run() {
		while (true) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			Integer in = list.remove();
			if (in != null)
				System.out.println("移除" + in);
		}
	}
}



Part4、线程属性和方法
---------------------------------------
1.Thread.currentThread():静态方法,获取当前执行的线程
2.setName()/getName():非静态方法,设定/获取线程名称
3.setPriority(1~10)/getPriority():设定/查看线程优先级

Part5、开启线程的另外一种方式:实现Runnable接口
---------------------------------------
1.定义接口Runnable的实现类MyRunnable
2.在实现类中重写方法run();
3.创建MyRunnable的实例 r
4.创建线程的时候,传入参数r:new Thread(r);
5.调用线程的start方法,开启线程

Part6、Runnable的好处
---------------------------------------
1.因为Runnable是接口,所以避免了Java单继承的局限性
2.接口降低耦合

Part7、线程的状态
---------------------------------------
1.BLOCKED //阻塞
2.NEW //新建
3.RUNNABLE //执行中
4.TERMTNATED //已终止
5.TIME_WAITING //限时等待
6.WAITING //等待

Part4,5,6,7 代码示例

public class Ts03 {

	public static void main(String[] args) {
		
		//Thread t = Thread.currentThread();
		//System.out.println(t.getName());
		//t.setPriority(newPriority)
		//System.out.println(t.getPriority());
		MyRunnable r = new MyRunnable();
		new Thread(r).start();
	}
}


class MyRunnable implements Runnable
{

	@Override
	public void run() {
		while(true)
		{			
			System.out.println("HelloWorld");
		}
	}
}
public class Ts03 {

	public static void main(String[] args) {
		
		//Thread t = Thread.currentThread();
		//System.out.println(t.getName());
		//t.setPriority(newPriority)
		//System.out.println(t.getPriority());
		MyRunnable r = new MyRunnable();
		new Thread(r).start();
	}
}


class MyRunnable implements Runnable
{

	@Override
	public void run() {
		while(true)
		{			
			System.out.println("HelloWorld");
		}
	}
}



Part8、熊吃蜂蜜问题
-------------------------------------------------------
100只蜜蜂.
每只蜜蜂一次生产蜂蜜量为1.
蜜罐的容量是20.
熊在蜜罐满了的时候一次性吃掉所有蜂蜜。
提示:蜜蜂生产蜂蜜时,如果蜜罐已满则等待,否则+1,notifyAll.
熊吃蜂蜜时,如果蜜罐已满则吃掉再notifyAll,否则,notifyAll.

Part9、和尚吃馒头问题
--------------------------------------------------------
100馒头
50个和尚,每个和尚一次只能吃一个馒头,但是最多只允许吃三个馒头。
看每个和尚各吃了多少馒头。



Part8,9代码示例

public class Ts02 {
	public static void main(String[] args) {
		
//		//蜂蜜罐
//		Jar jar = new Jar();
//		//熊
//		for (int i = 0; i < 2; i++) {			
//			new Thread(new Bear(jar)).start();
//		}
//		//蜜蜂
//		for (int i = 0; i < 100; i++) {
//			new Thread(new Bee(jar)).start();
//		}
		
		ManTouGuo m = new ManTouGuo();
		
		for (int i = 0; i < 50; i++) {
			
			new Thread(new Monk(m)).start();
		}
		
		
	}
}

//--------------------练习1---------------------------//
/*
 * 1.熊吃蜂蜜问题
		100只蜜蜂.
		每只蜜蜂一次生产蜂蜜量为1.
		蜜罐的容量是20.
		熊在蜜罐满了的时候一次性吃掉所有蜂蜜。
		提示:蜜蜂生产蜂蜜时,如果蜜罐已满则等待,否则+1,notifyAll.
			 熊吃蜂蜜时,如果蜜罐已满则吃掉再notifyAll,否则,notifyAll.
 */
class Jar{
	
	//罐子最大蜂蜜量
	public static final int MAX = 20;
	//罐子当前蜂蜜量
	public int count = 0;
		
	//增加蜂蜜
	public synchronized void add(){
		
		//超过20.停止生产
		while(count >= MAX){
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		//开始生产
		count ++;
		System.out.println("库存" + count);
		//通知所有等待线程工作
		notifyAll();
	}
	
	//吃蜂蜜
	public synchronized void eat(){
		
		//小于20,等待
		while(count <20)
		{
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//吃蜂蜜
		count = count - 20;
		//通知
		notifyAll();
	}
	
}



class Bee implements Runnable{

	Jar jar;
	
	public Bee(Jar jar){
		this.jar = jar;
	}
	
	@Override
	public void run() {
		
		while(true){
			jar.add();
			System.out.println("蜜蜂产密,剩余" + jar.count);
		}
	}	
}

class Bear implements Runnable{
	
	
	Jar jar;
	
	public Bear(Jar jar){
		this.jar = jar;
	}
	
	
	
	@Override
	public void run() {
		
		while(true){
			jar.eat();
			System.out.println("熊吃蜂蜜,蜂蜜剩余" + jar.count);
		}
	}	
}


//------------------------练习2-----------------------//
/*
 * 	2.和尚吃馒头问题
		100馒头
		50个和尚,每个和尚一次只能吃一个馒头,但是最多只允许吃三个馒头。
		看每个和尚各吃了多少馒头。
 */

//馒头锅
class ManTouGuo
{
	//剩余馒头总数
	public int remainMTCount = 100;
	
	//吃馒头
	public synchronized void eat()
	{
		if(remainMTCount > 0)
		{			
			remainMTCount --;
			System.out.println("被吃了一个,剩余" + remainMTCount + "个馒头!");
		}
	}
	
}

//和尚
class Monk implements Runnable
{
	//吃了多少个
	int eatCount = 0;
	//馒头锅
	ManTouGuo mtg;
	
	public Monk(ManTouGuo mtg)
	{
		this.mtg = mtg;
	}
	
	@Override
	public void run() {
		
		while( eatCount < 3 && mtg.remainMTCount >0 )
		{
			mtg.eat();			
			eatCount++;
		}
		System.out.println("我吃了" + eatCount + "个馒头!");
	}	
}

本文完!









猜你喜欢

转载自blog.csdn.net/xcvbxv01/article/details/80859979