买火车票问题

package com.lanou4g;

public class Demo01 {
	public static void main(String[] args) {
		TicketSale ticketSale = new TicketSale();
		Thread t1 = new Thread(ticketSale,"网上购票");
		Thread t2 = new Thread(ticketSale,"黄牛购票");
		Thread t3 = new Thread(ticketSale,"实窗购票");
		t1.start();
		t2.start();
		t3.start();
	}

}
class TicketSale implements Runnable{
	private int ticket =100;

	@Override
	public void run() {
		while (true) {
				if (ticket>0) {
					System.out.println(Thread.currentThread().getName()+"------"+ticket);
					ticket--;
				}else {
					break;
				}
		}
	Thread.yield();
		
	}
	
}

java多线程mq java多线程买票例子_java多线程mq

java多线程mq java多线程买票例子_同步代码块_02

从打印结果我们可以看出有两个问题:出现负数的情况 一张票卖几次;

很显然这时不符合要求的: 

原因:


1.CPU是随机执行线程的也就是说线程可以在方法的任何位置停止执行(利用极限位置去假设)  当一线程进入run方法 但是还未打印就被CPU抛弃处于就绪状态 ,另外的线程也进入run方法,那么就会出现一票多卖的情况.

2.当卖到最后一张票时 任在打印前被CPU抛弃,处于就绪状态,那么另外的线程任然可以通过判断进入run方法,没条线程结束都要进行递减,所以导致问题二出现;


解决办法:

根据上面出现的问题分析 :只要保证同一时间 只有一个线程操作共享数据就行;(当一个线程操作数据时 其他线程 不能操作,只能等着操作的线程 操作完毕以后 才能继续进来操作数据);

于是引入同步锁的概念;

同步代码块(同步锁)

  锁 任意的对象,但是保证只有一把锁(锁是唯一的 使用的同一把锁)

  synchronized(锁){

   操作共享数据的代码

  }

同步代码块是如何做到 一个线程执行代码时,其他线程在外面等着?

  同步代码块规程:

  当线程进入同步代码块时 先看一下有没有锁 

  如果有锁就进入同步代码块中执行代码 ,在进去的同时会获取这把锁 带着这把锁执行代码

  当代码执行完毕,出同步代码块时 将这把锁释放(还回去)

  

  如果没锁 :线程跟在同步代码块前等待(等着有锁猜能进)

public class Demo01 {
	public static void main(String[] args) {
		TicketRunable ticketRunable = new TicketRunable();
		Thread t1 = new Thread(ticketRunable);
		Thread t2 = new Thread(ticketRunable);
		Thread t3 = new Thread(ticketRunable);
		t1.start();
		t2.start();
		t3.start();
	}

}
/*class MyThread1 extends Thread{
	//声明票
	private int ticket = 50;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
	}
}*/
// 接口实现
class TicketRunable implements Runnable{
	//声明票  
		private int ticket = 50;
		//声明锁对象(保证只有一把锁)
		private Object oracle = new Object();
		//卖票
	@Override
	public void run() {
		//循环出票
		
		while (true) {
		
			synchronized (oracle) {

				if (ticket>0) {
					//为了让测试结果更明显 加个线程休眠
					try {
						Thread.sleep(300);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				
					//卖票
					System.out.println(Thread.currentThread().getName()+"*****"+ticket);
					//卖一张少一张.
					ticket--;
				}else {
					//卖光了 结束循环
					break;
				}
			
			}
			//让出CPU的执行资源(不一定让)
			Thread.yield();
		}
		
	}
	
}

同步方法:作用和同步代码块相同,并且也使用synchronized 关键字

  该关键字 声明在方法上.

  作用: 同一时间只能有一个线程 进入到同步方法中 执行代码

  方法中也是 操作共享数据的代码 

  同样 有锁 处理方式和同步代码块一样.

  同步的成员(对象)方法 锁是this(本类的对象)

  静态方法: 也同样可以加锁 锁是 类锁  类名.class (表示这个类)

  静态方法中不能使用this(方法中不一定有对象)

public class Demo02 {
	public static void main(String[] args) {
		TicketRunable1 ticketRunable1 = new TicketRunable1();
		Thread t1 = new Thread(ticketRunable1);
		Thread t2 = new Thread(ticketRunable1);
		Thread t3 = new Thread(ticketRunable1);
		t1.start();
		t2.start();
		t3.start();
	}

}
// 接口实现
class TicketRunable1 implements Runnable{
	//声明票  
		private static int ticket = 50;
		//卖票
	@Override
	public void run() {
		//循环出票 
		
		while (true) {
			
		if (fun()) {
			break;
		}
			//让出CPU的执行资源(不一定让)
			Thread.yield();
		}
		
	}
		private static synchronized boolean fun(){

			if (ticket>0) {
				//为了让测试结果更明显 加个线程休眠
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			
				//卖票
				System.out.println(Thread.currentThread().getName()+"*****"+ticket);
				//卖一张少一张.
				ticket--;
				return  false;
			}else {
				return true;
			}
			
		}
	
}

JDK1.5 出现的Lock 类

使用实现类 ReentrantLock

  Lock() 获取锁

  unLock 释放锁 保证锁一定会被释放

  使用前提: 和同步代码块一样 要保证 用的是同一把锁

  使用格式:

  try{

   操作共享数据的代码

  }finally{

    释放锁

  }

public class Demo03 {
	public static void main(String[] args) {
		TicketRunable2 ticketRunable2 = new TicketRunable2();
		Thread t1 = new Thread(ticketRunable2);
		Thread t2 = new Thread(ticketRunable2);
		Thread t3 = new Thread(ticketRunable2);
		t1.start();
		t2.start();
		t3.start();
	}

}

class TicketRunable2 implements Runnable {

	private int ticket = 50;
	// 声明锁 保证是同一把
	private ReentrantLock Lock = new ReentrantLock();

	@Override
	public void run() {

		while (true) {
			// 获取锁
			Lock.lock();
			try {

				if (ticket > 0) {
					try {
						Thread.sleep(300);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "*****" + ticket);
					ticket--;
				} else {
					break;
				}
				Thread.yield();

			} finally {
				Lock.unlock();
			}
		}
	}

}

简单应用:

package com.lanou3g;
/*
公司年会 进入公司有两个门(前门和后门)
进门的时候 每位人都能获取一张彩票(7位数)
公司有100个员工
利用多线程模拟进门过程
统计每个入口入场的人数 
每个人拿到的彩票的号码 要求7位数字 不能重复
打印格式:
编号为: 1 的员工 从后门 入场! 拿到的双色球彩票号码是: [17, 24, 29, 30, 31, 32, 07]
编号为: 2 的员工 从后门 入场! 拿到的双色球彩票号码是: [06, 11, 14, 22, 29, 32, 15]
…..
从后门 入场的员工总共: 45 位员工  
从前门 入场的员工总共: 55 位员工
保证总人数100即可
*/
import java.util.ArrayList;

public class Demo04 {
	public static void main(String[] args) {
		//创建测试线程
		GSRunnable runnable = new GSRunnable();
		//前门
		Thread t1 = new Thread(runnable,"前门");
		//后门
		Thread t2 = new Thread(runnable,"后门");
		//启动线程
		t1.start();
		t2.start();
	}
}

class GSRunnable implements Runnable{
	//声明共享数据
	private int pNum = 100;
	//记录前门进的人数
	private int qNum = 0;
	//记录后门进的人数
	private int hNum = 0;
	@Override
	public void run() {
		while (true) {
			synchronized (this) {

				//结束循环的条件
				if (pNum <= 0) {
					break;
				}
				//判断进来的线程是前门还是后门
				String name = Thread.currentThread().getName();
				if (name.equals("前门")) {
					qNum++;
					System.out.println("编号为:" + (100 - pNum + 1) + "的员工从前门入场,拿到的彩票是" + getLottery());
					pNum--;
				}else {
					hNum++;
					System.out.println("编号为:" + (100 - pNum + 1) + "的员工从后门入场,拿到的彩票是" + getLottery());
					pNum--;
				}
				//分别打印前后门员工人数
				if (pNum ==0) {
					System.out.println("从前门 入场的员工总共:"+qNum+" 位员工");
					System.out.println("从后门 入场的员工总共:"+hNum+" 位员工");
				}
			
			}
			//让出CPU资源
			Thread.yield();
		}
	}
	//打印彩票号
	public ArrayList<Integer> getLottery() {
		ArrayList<Integer> list = new ArrayList<>();
		//随机七个数 放入集合中
		//0-100
		while(list.size() < 7) {
			//随机数
			int random =(int)(Math.random()*(99 - 0 + 1) + 0 ); 
			//判断不存在就添加进集合
			if (!list.contains(random)) {
				list.add(random);
			}
		}
		//返回结果
		return list;
	}
}


线程死锁:


package com.lanou3g;/* * 线程死锁 * 前提: 1.必须要有同步锁的嵌套 * 2.锁对象唯一(使用同一把