Java多线程中存在线程同步问题。当线程a使用某一多线程共享的资源R时,线程b也使用资源R,就会出现问题。
先看一个例子:
本例设置3个售票窗口同时出售100张票,每个窗口出票间隔为1秒,直到卖完为止。
1 /* 2 * 功能:制造线程同步问题 3 */ 4 package com.miaoyu; 5 6 public class Demo10 { 7 public static void main(String[] args) { 8 // TODO Auto-generated method stub 9 TicketWindow tw1 = new TicketWindow(); 10 11 Thread t1 = new Thread(tw1); 12 Thread t2 = new Thread(tw1); 13 Thread t3 = new Thread(tw1); 14 t1.start(); 15 t2.start(); 16 t3.start(); 17 } 18 19 } 20 21 class TicketWindow implements Runnable 22 { 23 private int num = 100 ; 24 //间隔1s出票 25 26 @Override 27 public void run() { 28 // TODO Auto-generated method stub 29 while(true) 30 { 31 32 if(num>0) 33 { 34 try { 35 Thread.sleep(1000); 36 } catch (InterruptedException e) { 37 // TODO Auto-generated catch block 38 e.printStackTrace(); 39 } 40 System.out.println(Thread.currentThread().getName()+"正在售出第"+num+"张票"); 41 num--; 42 }else break; 43 } 44 } 45 }
运行结果:
问题:
由运行结果可以看出,可能存在同一张票被多个窗口出售的情况。
原因:
问题是由下边一段代码造成的。当线程1执行到System.out.println(Thread.currentThread().getName()+"正在售出第"+num+"张票");但还没有执行num--时,线程2也执行System.out.println,此时会输出同一个num。
1 if(num>0) 2 { 3 try { 4 Thread.sleep(1000); 5 } catch (InterruptedException e) { 6 // TODO Auto-generated catch block 7 e.printStackTrace(); 8 } 9 System.out.println(Thread.currentThread().getName()+"正在售出第"+num+"张票"); 10 num--; 11 }else 12 { 13 break; 14 }
解决方法:
要保证线程原子性,即一个线程使用资源时,其他线程处于阻塞状态,排队等待。
使用对象锁就可以轻松解决。
1 synchronized (this) 2 { 3 (需要同步的代码段) 4 }
使用对象锁之后的程序:
1 /* 2 * 功能:解决线程同步问题 3 */ 4 package com.miaoyu; 5 6 public class Demo10 { 7 public static void main(String[] args) { 8 // TODO Auto-generated method stub 9 TicketWindow tw1 = new TicketWindow(); 10 11 Thread t1 = new Thread(tw1); 12 Thread t2 = new Thread(tw1); 13 Thread t3 = new Thread(tw1); 14 t1.start(); 15 t2.start(); 16 t3.start(); 17 } 18 19 } 20 21 class TicketWindow implements Runnable 22 { 23 //定义剩余票数num 24 private int num = 100 ; 26 27 @Override 28 public void run() { 29 // TODO Auto-generated method stub 30 while(true) 31 { 32 //保证原子性,【同步代码块】 33 synchronized (this) 34 { 35 if(num>0) 36 { 37 try { 38 //出票间隔随机 39 Thread.sleep((int)(Math.random()*2000)); 40 } catch (InterruptedException e) { 41 // TODO Auto-generated catch block 42 e.printStackTrace(); 43 } 44 System.out.println(Thread.currentThread().getName()+"正在售出第"+num+"张票"); 45 num--; 46 }else break; 47 } 48 } 49 } 50 }
运行结果:
对Java同步机制的解释:
Java任意类型的对象都有一个标志位,该标志位具有0,1两种状态,其开始状态为1,当某个线程执行了synchronized(object)语句后,object对象的标志位变为0状态,直到执行完整个synchronized语句中的代码块后,该对象的标志位又回到1状态。
当一个线程执行到synchronized(object)语句的时候,先检查object对象的标志位,如果为0状态,表明已经有另外的线程正在执行synchronized包括的代码,那么这个线程将暂时阻塞,让出cpu资源,直到另外的线程执行完相关的同步代码,并将object对象的标志位变为1状态,这个线程等阻塞就取消,线程能继续运行,该线程又将object的标志位变为0状态,防止其他的线程再进入相关的同步代码块中。