Java 多线程同步-模拟窗口售票

Java 多线程同步-模拟窗口售票

java开发者 2018-11-02 20:52:45

Java 多线程同步-模拟窗口售票_第1张图片

Java

内容目录

  • 实现Runnable接口
  • 使用 同步代码块
  • 1、实现的方式
  • 2、继承的方式
  • 使用 同步方法
  • 1、实现的方式

本文例子:利用多线程模拟 3 个窗口卖票

 

实现Runnable接口

public class TestThread2 {
 public static void main(String [] args){
 Window window=new Window();
 Thread thread1=new Thread(window,"窗口一");
 Thread thread2=new Thread(window,"窗口二");
 Thread thread3=new Thread(window,"窗口三");
 thread1.start();
 thread2.start();
 thread3.start();
 }
}
class Window implements Runnable{
 int ticket=50;
 @Override
 public void run(){
 while (true){
 if(ticket > 0){
 try {
 Thread.currentThread().sleep(100);//模拟卖票需要一定的时间
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
 }else {
 break;
 }
 }
 }
}

运行结果:

窗口二售票,票号为:13
窗口三售票,票号为:12
窗口一售票,票号为:11
窗口二售票,票号为:10
窗口一售票,票号为:10
窗口三售票,票号为:10
窗口三售票,票号为:9
窗口一售票,票号为:8
窗口二售票,票号为:7
窗口三售票,票号为:6
窗口一售票,票号为:5
窗口二售票,票号为:4
窗口三售票,票号为:3
窗口一售票,票号为:2
窗口二售票,票号为:1
窗口三售票,票号为:0
窗口一售票,票号为:-1

结果分析:这里出现了票数为0和负数还有重票的情况,这在现实生活中肯定是不存在的,那么为什么会出现这样的情况呢?

当票号为10时:A线程、B线程、C线程同时进入到if(ticket > 0)的代码块中,A线程已经执行了打印输出语句,但是还没有做ticket--操作;

这时B线程就开始执行了打印操作,那么就会出现两个线程打印票数一样,即卖的是同一张票

当票号为1时:A线程、B线程,C线程同时进入到if(ticket > 0)的代码块中,A线程执行了打印语句,并且已经做完了ticket--操作,则此时ticket=0;

B线程再打印时就出现了0的情况,同理C线程打印就会出现-1的情况。

解决办法:即我们不能同时让超过两个以上的线程进入到 if(ticket > 0)的代码块中,不然就会出现上述的错误。必须让一个线程操作共享数据完毕以后,其他线程才有机会参与共享数据的操作。我们可以通过以下两个办法来解决:

1、使用 同步代码块

2、使用 同步方法

 

使用 同步代码块

synchronized(同步监视器){
 //需要被同步的代码块(即为操作共享数据的代码)
}

同步监视器:由任意一个类的对象来充当,哪个线程获取此监视器,谁就执行大括号里被同步的代码。俗称:锁

要求:1、所有的线程必须公用同一把锁!不能相对于线程是变化的对象;

2、并且只需锁住操作共享数据的代码,锁多了或少了都不行;

实例:

1、实现的方式

public class TestWindow {
 public static void main(String [] args){
 Window1 window=new Window1();
 Thread thread1=new Thread(window,"窗口一");
 Thread thread2=new Thread(window,"窗口二");
 Thread thread3=new Thread(window,"窗口三");
 thread1.start();
 thread2.start();
 thread3.start();
 }
}
class Window1 implements Runnable{
 int ticket=100;//共享数据
 @Override
 public void run(){
 while (true){
 synchronized (this){//this表示当前对象,此时表示创建的 window
 if(ticket > 0){
 try {
 //模拟卖票需要一定的时间
 Thread.sleep(100);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
 }
 }
 }
 }
}

注意:在实现的方式中,考虑同步的话,可以使用this充当锁,但在继承的方式中,会创建多个对象,慎用this

2、继承的方式

public class TestWindow1 {
 public static void main(String [] args){
 Window2 window1=new Window2();
 Window2 window2=new Window2();
 window1.start();
 window2.start();
 }
}
class Window2 extends Thread{
 static int ticket=100;//共享数据;注意声明为 static,表示几个窗口共享
 static Object object=new Object();//用static 可以表示唯一
 @Override
 public void run(){
 while (true){
 //synchronized (this){//this表示当前对象,此时表示创建的 window1和window2
 synchronized (object){//锁必须是唯一,不能每个线程都使用自己的一把锁
 if(ticket > 0){
 try {
 Thread.sleep(200);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
 }
 }
 }
 }
}

注意:1、继承的方式会创建多个实例,所以共享资源需要用static来修饰,表示共享

2、继承的方式会创建多个实例,所以 this 表示不同的实例对象,这里表示widow1和window2,所以不能使用this当锁,此时可以定义一个 static 修饰的对象当锁

使用 同步方法

语法:即用 synchronized 关键字修饰方法

将操作共享数据的方法声明为synchronized。即此方法为同步方法,能够保证当其中一个线程执行此方法时,其他线程再外等待直至此线程执行完此方法。

注意:同步方法的锁:this

实例:

1、实现的方式

public class TestWindow2 {
 public static void main(String [] args){
 Window3 window=new Window3();
 Thread thread1=new Thread(window,"窗口一");
 Thread thread2=new Thread(window,"窗口二");
 Thread thread3=new Thread(window,"窗口三");
 thread1.start();
 thread2.start();
 thread3.start();
 }
}
class Window3 implements Runnable{
 int ticket=100;//共享数据
 @Override
 public void run(){
 while (true){
 show();
 }
 }
 public synchronized void show(){//this充当锁,此时表示创建的 window;
 // 如果用继承的方式,使用同步方法,这里表示创建的 window1和window2,继承的方式不要使用同步方法
 if(ticket > 0){
 try {
 Thread.currentThread().sleep(100);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
 }
 }
}

注意:1、synchronized 的锁为this,这里表示创建的对象实例window;

2、继承的时候t this 表示创建的window1和window2,继承的方式不要使用同步方法。

加Java架构师进阶交流群获取Java工程化、高性能及分布式、高性能、深入浅出。高架构。性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点高级进阶干货的直播免费学习权限 都是大牛带飞 让你少走很多的弯路的 群.号是:338549832 对了 小白勿进 最好是有开发经验

注:加群要求

、具有工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加。

、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加。

3如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的,可以加。

4、觉自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在技术领域继续突破的可以加。

5.阿Java高级大牛直播讲解知识点,分享知识,多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知!

你可能感兴趣的:(多线程/高并发)