【深入操作系统】线程问题模型(二)

81 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第26天,点击查看活动详情

初版代码示例

class SynStack
{
	private char[] data = new char[6];
	private int cnt = 0;	
	public void push(char ch)
	{
		data[cnt]=ch;
		++cnt;
		System.out.println("1111");	
	}
	public char pop()
	{
		--cnt;
		System.out.printf("2222");
		return data[cnt];	
	}
}
class Producer implements Runnable
{
	private SynStack ss=null;
	public Producer(SynStack ss)
	{
		this.ss=ss;	
	}
	public void run()
	{
		// push('a');//错误
		try
		{
			Thread.sleep(2000);
		}	
		catch(Exception e)
		{
		}
		ss.push('a');
		
	}
}
class Consumer implements Runnable
{
	private SynStack ss=null;
	public Consumer(SynStack ss)
	{
		this.ss=ss;	
	}
	public void run()
	{
		System.out.printf("%c\n",ss.pop());
	}
}
public class TestPC
{
	public static void main(String[] args)
	{
		SynStack ss = new SynStack();
		Producer p = new Producer(ss);
		Consumer c = new Consumer(ss);	
		Thread t1=new Thread(p);
		Thread t2=new Thread(c);
		t1.start();
		t2.start();
	}	
	
}

PS:

  1. public void run()// throws Exception //错误 Runnable里没有异常处理
  2. t2.start();//错误 因为无法判断是先进还是先出,如果先进则没错 但如果先出 则现在栈中没有元素 导致数组下标越界

结果:

  • 报数组溢出错误

解释:

  • 我们观察程序,发现我们创造了一个栈 有两个方法 分别是pop()与push() 出栈与入栈,这个栈其实就是 糖果池 而糖果池的最大容量就是 这个栈的最大值6
  • 之后我们创建了生产者与消费者且他们都实现了Runnable接口与run方法,生产者会在run方法中push()一个元素 也就是增加一个糖果,消费者会在run方法中pop()一个元素 也就是减少一个糖果。
  • 最后我们创建了生产线程与消费线程,我们令生产线程的生产速率小于消费线程,最终会发现当糖果池为0后消费线程还在消费,导致溢出错误。

解决方案代码示例

class SynStack
{
	private char[] data = new char[6];
	private int cnt = 0;	
	
	public synchronized void push(char ch)
	{
		while(cnt==data.length)
		{
			try
			{
				this.wait();	
			}	
			catch(Exception e)
			{}
		}
		this.notify();
		data[cnt]=ch;
		++cnt;
		System.out.printf("生产线程正在生产第%d个产品,该产品是:%c\n",cnt,ch);	
	}
	
	
	public synchronized char pop()
	{
		char ch;
		if(cnt==0)
		//注意:这里的注意同上
		{
			try
			{
				this.wait();	
			}	
			catch(Exception e)
			{}
		}
		this.notify();
		ch=data[cnt-1];
		System.out.printf("消费线程正在消费第%d个产品,该产品是:%c\n",cnt,ch);	
		cnt--;
		return ch;	
	}
}


class Producer implements Runnable
{ 
	private SynStack ss=null;
	
	public Producer(SynStack ss)
	{
		this.ss=ss;	
	}
	
	public void run()// throws Exception 
	{
		// push('a');//错误
		char ch;
		for(int i=0;i<20;i++)
		{
			try
			{
				Thread.sleep(200);
			}	
			catch(Exception e)
			{
			}
			ch=(char)('a'+i);
			ss.push(ch);
		}
	}
}


class Consumer implements Runnable
{
	private SynStack ss=null;
	
	public Consumer(SynStack ss)
	{
		this.ss=ss;	
	}
	
	public void run()
	{
		for(int i=0;i<20;i++)
		{
			/*try
			{
				Thread.sleep(200);
			}	
			catch(Exception e)
			{
			}*/
			ss.pop();
		}
	}
}


public class TestPC_2
{
	public static void main(String[] args)
	{
		SynStack ss = new SynStack();
		Producer p = new Producer(ss);
		Consumer c = new Consumer(ss);	
		Thread t1=new Thread(p);
		Thread t2=new Thread(c);
		t1.start();
		t2.start();
	}	
}

PS:

  1. while(cnt==data.length) //注意: //这里用while是为了保证 栈为满而暂停,然后下一次如果再满还是再这个循环里 保证再暂停, //如果改成if 则只能暂停一次 下一次从暂停的那个位置继续走

    //但如果是同步过以后则可以用 if,因为如果满 则暂停(wait)生产线程 同时解除霸占,转换到 //消费线程 进行消费操作 并锁住消费线程对象 导致一定会运行完消费操作 使得栈不为满 之后 //唤醒生产操作 并结束消费程序 解锁,之后再竞争

    // 但这种while与if都可以用的情况 仅在 只有两个相互依赖的线程 的条件下成立,我们可以想一想 如果我们现在来一个毫不相干的线程c c运行完后也 // 会唤醒某个线程 这样不满足条件的线程依旧可能被唤醒 并且因为是if 所以直接跳出if进行生产操作 导致错误

    //使用wait会解锁,sleep则不会

  2. this.notify();//唤醒其他某一个暂停的线程 对于这个例子 就是唤醒消费线程

  3. this.notify();//唤醒其他某一个暂停的线程 对于这个例子 就是唤醒生产线程

  4. public void run()// throws Exception //错误 Runnable里没有异常处理

  5. Thread.sleep(200);//这种是生产比较慢 消费快

  6. Thread.sleep(200);//这种是消费比较慢 生产快

结果:

  • 生产线程正在生产第1个产品,该产品是:a
  • 消费线程正在消费第1个产品,该产品是:a
  • 生产线程正在生产第1个产品,该产品是:b
  • 消费线程正在消费第1个产品,该产品是:b
  • 生产线程正在生产第1个产品,该产品是:c
  • 消费线程正在消费第1个产品,该产品是:c
  • 生产线程正在生产第1个产品,该产品是:d
  • 消费线程正在消费第1个产品,该产品是:d
  • 生产线程正在生产第1个产品,该产品是:e
  • 消费线程正在消费第1个产品,该产品是:e
  • 生产线程正在生产第1个产品,该产品是:f
  • 消费线程正在消费第1个产品,该产品是:f
  • 生产线程正在生产第1个产品,该产品是:g
  • 消费线程正在消费第1个产品,该产品是:g
  • 生产线程正在生产第1个产品,该产品是:h
  • 消费线程正在消费第1个产品,该产品是:h
  • 生产线程正在生产第1个产品,该产品是:i
  • 消费线程正在消费第1个产品,该产品是:i
  • 生产线程正在生产第1个产品,该产品是:j
  • 消费线程正在消费第1个产品,该产品是:j
  • 生产线程正在生产第1个产品,该产品是:k
  • 消费线程正在消费第1个产品,该产品是:k
  • 生产线程正在生产第1个产品,该产品是:l
  • 消费线程正在消费第1个产品,该产品是:l
  • 生产线程正在生产第1个产品,该产品是:m
  • 消费线程正在消费第1个产品,该产品是:m
  • 生产线程正在生产第1个产品,该产品是:n
  • 消费线程正在消费第1个产品,该产品是:n
  • 生产线程正在生产第1个产品,该产品是:o
  • 消费线程正在消费第1个产品,该产品是:o
  • 生产线程正在生产第1个产品,该产品是:p
  • 消费线程正在消费第1个产品,该产品是:p
  • 生产线程正在生产第1个产品,该产品是:q
  • 消费线程正在消费第1个产品,该产品是:q
  • 生产线程正在生产第1个产品,该产品是:r
  • 消费线程正在消费第1个产品,该产品是:r
  • 生产线程正在生产第1个产品,该产品是:s
  • 消费线程正在消费第1个产品,该产品是:s
  • 生产线程正在生产第1个产品,该产品是:t
  • 消费线程正在消费第1个产品,该产品是:t

解释:

因为消费线程比较快,所以现在的情况就是生产一个立马就被消费了