JAVA------线程同步(案例详解)

先看一个案例:

需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票


思路:

  1. 定义一个类ticket实现Runnable接口,
    里面定义一个成员变量: private int tickets= 100;
  2. 在ticket类中重写run()方法实现卖票, 代码步骤如下:
    A:判断票数大于0,就卖票,并告知是哪个窗口卖的
    B:卖了票之后,总票数要减1
    C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行
  3. 定义一个测试类, 里面有main方法,代码步骤如下
    A:创建SellTicket类的对象
    B:创建三个Thread类的对象,把SelITicke对象作为构造方法的参数,
    并给出对应的窗口名称
    C:启动线程

先看一个初步的实现:
ticket类:

package 卖票;
//定义一个类SellTicket实现Runnable接口,
//里面定义一个成员变量: private int tickets= 100;

public class ticket implements Runnable{
    
    
	private int tickets=100;
	
	@Override
	public void run() {
    
    
		//A:判断票数大于0,就卖票,并告知是哪个窗口卖的
		//B:卖了票之后,总票数要减1
		//C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行
		while(true) {
    
    
			if(tickets>0) {
    
    
				int temp=101-tickets;
				System.out.println(Thread.currentThread().getName()+"正在出售第"+temp+"张票");
				tickets--;
			}
		}
	}

}

main类:

package 卖票;

/*
 * 需求:某电影院目前正在上映国产大片,共有100张票,
 * 而它有3个窗口卖票,请设计一个程序模拟该电影院卖票

思路:
1.	定义一个类SellTicket实现Runnable接口,
 	里面定义一个成员变量: private int tickets= 100;
2.	在ellTicket类中重写run()方法实现卖票, 代码步骤如下
A:判断票数大于0,就卖票,并告知是哪个窗口卖的
B:卖了票之后,总票数要减1
C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行

3.	定义一个测试类, 里面有main方法,代码步骤如下
A:创建SellTicket类的对象
B:创建三个Thread类的对象,把SelITicke对象作为构造方法的参数,
	并给出对应的窗口名称
C:启动线程
 */


public class ticketDemo {
    
    
	public static void main(String[] args) {
    
    
		ticket t=new ticket();
		
		Thread t1=new Thread(t,"窗口一");
		Thread t2=new Thread(t,"窗口二");
		Thread t3=new Thread(t,"窗口三");
		
		t1.start();
		t2.start();
		t3.start();
		
	}

}


运行完会发现这个程序存在两个问题:
1.出现负数的票
2.一张票卖多次

出现问题满足的三个条件:

  1. 是否是多线程环境
  2. 是否有数据共享
  3. 是否有多条语句操作共享数据

显然这里只能破坏第三个条件


解决方法:把多条语句操作共享数据的代码锁起来,让任意时刻只能有一个线程执行(也就是操作系统中讲的互斥访问

  • JAVA正好提供了同步代码块的方式来解决
  • 同步代码块格式:
synchronized (任意对象) {
    
    //相当于给代码加锁,任意对象就是一把锁
多条语句操作共享数据的代码
}

下面看优化后的代码:
这里加上了sleep()方法模拟出票时间


ticket类:

package 卖票;
//定义一个类SellTicket实现Runnable接口,
//里面定义一个成员变量: private int tickets= 100;

public class ticket implements Runnable{
    
    
	private int tickets=100;
	private Object obj=new Object();
	
	@Override
	public void run() {
    
    
		//A:判断票数大于0,就卖票,并告知是哪个窗口卖的
		//B:卖了票之后,总票数要减1
		//C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行
		while(true) {
    
    
			synchronized (obj) {
    
    
			if(tickets>0) {
    
    
				//通过sleep()方法模拟出票时间
				try {
    
    
					Thread.sleep(100);
				} catch (Exception e) {
    
    
					e.printStackTrace();
				}
				int temp=101-tickets;
				System.out.println(Thread.currentThread().getName()+"正在出售第"+temp+"张票");
				tickets--;
			}
		}
	  }
	}

}

main类:

package 卖票;

/*
 * 需求:某电影院目前正在上映国产大片,共有100张票,
 * 而它有3个窗口卖票,请设计一个程序模拟该电影院卖票

思路:
1.	定义一个类SellTicket实现Runnable接口,
 	里面定义一个成员变量: private int tickets= 100;
2.	在ellTicket类中重写run()方法实现卖票, 代码步骤如下
A:判断票数大于0,就卖票,并告知是哪个窗口卖的
B:卖了票之后,总票数要减1
C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行

3.	定义一个测试类, 里面有main方法,代码步骤如下
A:创建SellTicket类的对象
B:创建三个Thread类的对象,把SelITicke对象作为构造方法的参数,
	并给出对应的窗口名称
C:启动线程
 */


public class ticketDemo {
    
    
	public static void main(String[] args) {
    
    
		ticket t=new ticket();
		
		Thread t1=new Thread(t,"窗口一");
		Thread t2=new Thread(t,"窗口二");
		Thread t3=new Thread(t,"窗口三");
		
		t1.start();
		t2.start();
		t3.start();
		
	}

}


由此便实现了三个窗口卖票,票不重复且不为负值,最主要的就是要对卖票这一过程加锁,实现互斥访问。

猜你喜欢

转载自blog.csdn.net/weixin_45102820/article/details/113762087