Java多线程同步锁问题

本文通过一个典型的多线程影院售票程序,演示了多线程环境下可能出现的问题,如重复售票和负数票等问题,并详细解释了这些问题产生的原因。接着介绍了如何使用Java的同步机制来解决这些问题,包括同步代码块和同步方法的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、引入问题
1.电影院卖票程序
[java]  view plain  copy
  1. /* 
  2.  * 电影院卖100张电影票,一共3个窗口同时卖票。 
  3.  */  
  4. public class MyRunnable implements Runnable{  
  5.     private int tickets=100;  
  6.       
  7.     public void run() {  
  8.         while(true) {  
  9.             if(tickets>0) {  
  10.                 try {  
  11.                     Thread.sleep(100);  
  12.                 } catch (InterruptedException e) {  
  13.                     // TODO Auto-generated catch block  
  14.                     e.printStackTrace();  
  15.                 }  
  16.                 System.out.println(Thread.currentThread().getName()  
  17.                 +"出售第:"+tickets--+"张票");              
  18.             }else {  
  19.                 break;  
  20.             }  
  21.         }  
  22.     }  
  23. }  
  24.   
  25.   
  26. public class SellTicketDemo {  
  27.     public static void main(String[] args) {  
  28.         // TODO Auto-generated method stub  
  29.         MyRunnable my = new MyRunnable();  
  30.         Thread th1 = new Thread(my);  
  31.         Thread th2 = new Thread(my);  
  32.         Thread th3 = new Thread(my);  
  33.           
  34.         th1.setName("窗口1");  
  35.         th2.setName("窗口2");  
  36.         th3.setName("窗口3");  
  37.           
  38.         th1.start();  
  39.         th2.start();  
  40.         th3.start();  
  41.     }  
  42. }  
2.电影院卖票程序出问题
A:为了更符合真实的场景,加入了休眠100毫秒。
B:运行代码后会发现问题
a:同票多次
原因:CPU一次操作的原子性
例如:卖第100张票,当票还在卖,没有减去1张时,另一个线程
进来了,它看到的票数还是原来的100,这就会出现同票问题。
b:负数票
原因:随机性和延迟
例如:当票还剩一张时,三个线程都进来了,只不过正在休息sleep();
当第一个线程卖出去后,就剩0张,后两个线程就会显示卖出0和-1张票。

3.多线程安全问题的原因(也是我们以后判断一个程序是否有线程安全问题的依据)
A:是否有多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据


4.解决问题:
由上面可见,多线程出现问题的三种原因全部满足。
A和B问题改变不了,那就只能想办法把C改变一下。
思想:
把多条语句操作共享数据的代码包成一个整体,让某个线程正在执行的
时候,别的线程不能来执行。(就像有一个房间,一个人进去把门锁了,别人
就不能进来,除非那个人出来别人才能抢进去的机会。)

如何包住代码呢?使用Java提供的同步机制。



二、同步代码块
synchornized(对象){
需要同步的代码;
}


A:对象是什么?
可以随便创建一个对象例如Object obj;
B:需要同步的代码是什么?
多条语句操作共享数据的那部分代码
注意:
同步可以解决安全问题的根本原因就在那个对象上面。该对象如同锁的功能。

[java]  view plain  copy
  1. public class MyRunnable implements Runnable{  
  2.     private int tickets=100;  
  3.     private Object obj = new Object();  
  4.       
  5.     public void run() {  
  6.         while(true) {  
  7.             synchronized(obj) {  
  8.                 if(tickets>0) {  
  9.                     try {  
  10.                         Thread.sleep(100);  
  11.                     } catch (InterruptedException e) {  
  12.                         // TODO Auto-generated catch block  
  13.                         e.printStackTrace();  
  14.                     }  
  15.                       
  16.                     System.out.println(Thread.currentThread().getName()+"出售:"+tickets--);                               }else {  
  17.                     break;  
  18.                 }  
  19.             }  
  20.         }  
  21.     }  
  22. }  
  23.   
  24. public class SellTicketDemo {  
  25.     public static void main(String[] args) {  
  26.         // TODO Auto-generated method stub  
  27.         MyRunnable my = new MyRunnable();  
  28.         Thread th1 = new Thread(my);  
  29.         Thread th2 = new Thread(my);  
  30.         Thread th3 = new Thread(my);  
  31.           
  32.         th1.setName("窗口1");  
  33.         th2.setName("窗口2");  
  34.         th3.setName("窗口3");  
  35.           
  36.         th1.start();  
  37.         th2.start();  
  38.         th3.start();  
  39.     }  
  40. }  


三、同步的特点及利弊
特点: 多个线程
  多个线程使用同一个锁对象
好处: 解决多线程安全问题

弊端:当线程很多时,因为每个线程都会去判断同步锁,耗费资源,效率低。

                 例如: StringBuffer,Vector,Hashtable
 Tips:如何把一个线程不安全的集合类变成一个线程安全的集合类
  用Collections工具类的方法即可。
  // public static <T> Collection<T> synchronizedCollection(Collection<T> c)

                            List<String> list = Collection.synchronizedList(new ArrayList<String>());


四、同步方法
格式:把同步关键字加在方法上
[java]  view plain  copy
  1.     public void run() {  
  2.         while(true) {  
  3.             sellTikets();  
  4.         }  
  5.     }  
  6.     private synchronized void sellTikets() {  
  7.         // TODO Auto-generated method stub  
  8.         if(tickets>0) {  
  9.             try {  
  10.                 Thread.sleep(100);  
  11.             } catch (InterruptedException e) {  
  12.                 // TODO Auto-generated catch block  
  13.                 e.printStackTrace();  
  14.             }  
  15.             System.out.println(Thread.currentThread().getName()+"出售:"+tickets--);                 
  16.         }  
  17.     }     
         锁对象是this

同步方法可以静态修饰
静态方法的锁对象是:类的字节码文件对象。
synchronized(MyRunnable.class);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值