📋 个人简介
- 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜
- 📝 个人主页:馆主阿牛🔥
- 🎉 支持我:点赞👍+收藏⭐️+留言📝
- 📣 系列专栏:java 小白到高手的蜕变🍁
- 💬格言:要成为光,因为有怕黑的人!🔥
目录
前言
上节我们写了一个比较经典的多窗口卖票案例,但我们最后发现他有重票和错票的情况,这是由于线程的不安全造成的,本节我将总结一下用同步的方式来处理线程安全问题!主要就是synchronized这个关键字。
问题概述
创建三个窗口卖票,总票数为100张。
1.问题:卖票过程中,出现了重票、错票﹣->出现了线程的安全问题
2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来。
3.如何解决:当一个线程 a 在操 ticket 的时候,其他线程不能参与进来。直到线程 a 操作完成后,其他线程才可以开始操作 ticket 。这种情況即使线程 a 出现了阻塞,也不能被改变。
同步代码块方式解决线程安全问题
synchronized(同步监视器){//需要被同步的代码}
说明:
1.操作共享数据的代码,即为需要被同步的代码
2.共享数据:多个线程共同繰作的变量。比如: ticket 就是共享数据。
3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。要求:多个线程必须要共用同一把锁。
4. 在实现 Runnable 接口创建多线程的方式中,我们可考虑使用 this 充当同步监视器。
注:当然为了方便,这个锁我们一般可以用this(还要看是继承Thread还是实现Runnable方式,继承Thread会有多个对象,此时this作为锁不唯一)、类名.class。
这里在说明一下类名.class。不是说这个锁是任何一个类的对象吗,其实,在java中,类也是一个对象。在 Java 中,每个 class 都有一个相应的 Class 对
象。也就是说,当我们编写一个类,编译完成后,在生成的.class 文件中,就会产生一个 Class 对象,用于表示这个类的类型信息。
其实任何一个类,都会有一个 Class 对象于这个类对像,在这个 Class 对象中,保存着实例化该类时所需要的基本信息, 类名.class 其实返回的是一个类的 Class 对象。
同步代码块解决继承Thread类的线程安全问题
publicclassSellTickets{publicstaticvoidmain(String[] args){MyTh t1 =newMyTh();MyTh t2 =newMyTh();MyTh t3 =newMyTh();
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();}}classMyThextendsThread{privatestaticint tickets =100;//这里总票数一定要是静态的(static),因为多个线程要共享这一个变量!//private static Object obj = new Object(); //可以作为锁@Overridepublicvoidrun(){while(true){// synchronized (obj)// 或者synchronized(MyTh.class){//这里锁不能用this,有三个对象,this作为锁不是共享的if(tickets>0){System.out.println(this.getName()+"-票号为:"+ tickets);
tickets--;}else{break;}}}}}
可以看到此时结果无重票。
同步代码块解决实现Runnable接口的线程安全问题
package 多线程;publicclassSellTickets{publicstaticvoidmain(String[] args){MyTh m =newMyTh();Thread t1 =newThread(m);Thread t2 =newThread(m);Thread t3 =newThread(m);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();}}classMyThimplementsRunnable{privateint tickets =100;//这里总票数不必一定要是静态的(static),因为我们是实现的Runnable接口,最后只创建一个对象//private static Object obj = new Object(); //可以作为锁@Overridepublicvoidrun(){while(true){// synchronized (obj)// synchronized (MyTh.class)synchronized(this){//此时当前类的对象唯一,可以用this作为锁if(tickets>0){System.out.println(Thread.currentThread().getName()+"-票号为:"+ tickets);
tickets--;}else{break;}}}}}
同步方法解决线程安全问题。
将你要进行同步的代码放到同步方法里就行。同步方法要用synchronized修饰!
关于同步方法的总结:
1.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
2.非静态的同步方法,同步监视器是: this ;静态的同步方法,同步监视器是:当前类本身。
同步方法解决继承Thread类的线程安全问题
publicclassSellTickets{publicstaticvoidmain(String[] args){MyTh t1 =newMyTh();MyTh t2 =newMyTh();MyTh t3 =newMyTh();
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();}}classMyThextendsThread{privatestaticint tickets =100;@Overridepublicvoidrun(){while(true){Sell();if(tickets<=0){break;}}}publicstaticsynchronizedvoidSell(){//此时,同步方法可以必须是静态的,同步监视器(锁)是MyTh.class(static方法里面不能有this)。因为锁不能为this。if(tickets>0){System.out.println(Thread.currentThread().getName()+"-票号为:"+ tickets);
tickets--;}}}
同步方法解决实现Runnable接口的线程安全问题
publicclassSellTickets{publicstaticvoidmain(String[] args){MyTh m =newMyTh();Thread t1 =newThread(m);Thread t2 =newThread(m);Thread t3 =newThread(m);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();}}classMyThimplementsRunnable{privateint tickets =100;//这里总票数不必一定要是静态的(static),因为我们是实现的Runnable接口,最后只创建一个对象@Overridepublicvoidrun(){while(true){Sell();if(tickets<=0){break;}}}publicsynchronizedvoidSell(){//此时,同步方法可以是非静态的,同步监视器(锁)是thisif(tickets>0){System.out.println(Thread.currentThread().getName()+"-票号为:"+ tickets);
tickets--;}}}
结语
如果你觉得博主写的还不错的话,可以关注一下当前专栏,博主会更完这个系列的哦!也欢迎订阅博主的其他好的专栏。
🏰系列专栏
👉软磨 css
👉硬泡 javascript
👉flask框架快速入门
版权归原作者 馆主阿牛 所有, 如有侵权,请联系我们删除。