多线程出现安全问题
问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式:同步机制
方式一:同步代码块
synchronized(同步监视器){ //需要被同步的代码 }
说明:
1.操作共享数据的代码,即为需要被同步的代码。
2.共享数据:多个线程共同操作的变量,比如: 车票就是共享数据。
3.同步监视器,俗称:锁,任何一个类的对象,都可以充当锁。(但是要求:多个线程必须共用同一把锁,即同一个对象)(可以考虑用this充当)(可以考虑类.class
充当)
示例:
①Runnable接口实现类
public class MThread implements Runnable{ private int ticket = 100; Object obj = new Object(); @Override public void run() { while (true) { synchronized (obj) { if (ticket > 0) { System.out.println(Thread.currentThread().getName() + ":票号为" + ticket); ticket--; } else break; } } } }
②创建并执行多线程
public class TestThread { public static void main(String[] args) { MThread thread = new MThread(); Thread curThread1 = new Thread(thread); Thread curThread2 = new Thread(thread); Thread curThread3 = new Thread(thread); curThread1.setName("窗口一"); curThread2.setName("窗口二"); curThread3.setName("窗口三"); curThread1.start(); curThread2.start(); curThread3.start(); } }
同步的方式,解决了线程的安全问题。
操作同步代码时,只能有一 个线程参与, 其他线程等待,相当于是一个单线程的过程,效率低。
注意:在继承Thread类这种情况下,需要将同步同步监视器声明为静态的,这样才能保证同一把锁。
方式一:同步方法
synchronized还可以放在方法声明中,表示整个方法为同步方法。
public synchronized void show (){ //其实是有一个同步监视器(锁)this //需要被同步的代码 }
与同步代码块使用类似,只是在run()方法中调用该同步方法。
注意:和使用同步代码块一样,在继承Thread类这种情况下,需要将该同步方法声明为静态的,即public static synchronized void show ()
,此时的同步监视器相当于是当前类.class
。
同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
非静态的同步方法,同步监视器是: this
静态的同步方法,同步监视器是:当前类本身