Java线程的同步机制可以分为两种方式:同步代码块和同步方法
以模拟火车票售票窗口为例:模拟火车站售票窗口,开启三个窗口售票,总票数为100张
理想状态下是:线程t1、t2、t3共同取这100张票,取完就停止
但这样也会存在极端状态,线程可能会在进入if语句后被挂起或者sleep后,三个线程可能会同时进入,当剩1张票的时候,可能就会打印出1、0、-1的错票情况
于是同步机制真是决定这个问题的关键所在!
Note:
1.线程安全问题存在的原因? 由于一个线程在操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在了安全问题。 2.如何解决线程的安全问题? 必须让一个线程操作共享数据完毕以后,其他线程才有机会参与共享数据的操作。 3.java如何实现线程的安全:线程的同步机制 方式一:同步代码块 synchronize(同步监视器){ //需要被同步的代码块(即为操作共享数据的代码) } 1.共享数据:多个线程共同操作的同一个数据(变量) 2.同步监视器:由任何一个类的对象来充当。哪个线程获取此监视器,谁就执行大括号里被同步的代码。俗称:锁 要求:所有的线程必须同用一把锁! 注:在实现的方式中,考虑同步的话,可以使用this来充当锁,但是在继承的方式中,慎用this!(考虑this是否只有一个对象) 方式二:同步方法 将操作共享数据的方法声明为synchronize。即此方法为同步方法,能够保证当其中一个线程执行 此方法时,其他线程在外等待直至此线程执行完此方法 >同步方法的锁:this 4.线程的同步的弊端:由于同一个时间只能有一个线程访问共享数据,效率变低了。
同步代码块(实现的方式):
public class TestWindow2 { public static void main(String[] args) { Window2 w = new Window2(); Thread thread1 = new Thread(w); Thread thread2 = new Thread(w); Thread thread3 = new Thread(w); thread1.setName("窗口1"); thread2.setName("窗口2"); thread3.setName("窗口3"); thread1.start(); thread2.start(); thread3.start(); } } class Window2 implements Runnable { private int ticket = 100; //共享数据 // Object obj = new Object(); @Override public void run() { // Animal a = new Animal(); //局部变量 while (true) { synchronized (this) { //this表示当前对象,本题中即为w if (ticket > 0) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "票号:" + ticket--); } } } } } class Animal { }
结果:
窗口1票号:100
窗口1票号:99
窗口3票号:98
....
窗口3票号:6
窗口3票号:5
窗口1票号:4
窗口1票号:3
窗口1票号:2
窗口3票号:1
同步代码块(继承的方式):
public class TestWindow3 { public static void main(String[] args) { Window3 window1 = new Window3(); Window3 window2 = new Window3(); Window3 window3 = new Window3(); window1.setName("窗口1"); window2.setName("窗口2"); window3.setName("窗口3"); window1.start(); window2.start(); window3.start(); } } class Window3 extends Thread { private static int ticket = 100; private static final Object o = new Object(); @Override public void run() { while (true) { // synchronized (this) { //在本问题中,this表示:window1 window2 window3 synchronized (o) { //在本问题中,this表示:window1 window2 window3 if (ticket > 0) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售出的票号为:" + ticket--); } } } } }
结果:同上
同步方法(实现的方式):
public class TestWindow4 { public static void main(String[] args) { Window4 w = new Window4(); Thread thread1 = new Thread(w); Thread thread2 = new Thread(w); Thread thread3 = new Thread(w); thread1.setName("窗口1"); thread2.setName("窗口2"); thread3.setName("窗口3"); thread1.start(); thread2.start(); thread3.start(); } } class Window4 implements Runnable { private int ticket = 100; //共享数据 @Override public void run() { while (true) { show(); } } public synchronized void show() { if (ticket > 0) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "票号:" + ticket--); } } }
结果:同上
同步方法(继承的方式):同步方法的锁默认是this,所以在继承的方式中也要慎用同步方法!