熟悉 Java 的多线程的一般都知道会有数据不一致的情况发生,比如两个线程在操作同一个类变量时,而保护数据不至于错乱的办法就是让方法同步或者代码块同步。同步时非原子操作就得同步,比如一个简单的 1.2+1 运算也该同步,以保证一个代码块或方法成为一个原子操作。
简单点说就是给在多线程环境中可能会造成数据破坏的方法,做法有两种,以及一些疑问:
1. 不论是静态的或非静态的方法都加上 synchronized 关键字,那静态的方法和非静态的方法前加上 synchronized 关键字有区别吗?
2. 或者在可疑的代码块两旁用 synchronized(this) 或 synchronized(someObject) 包裹起来,而选用 this 还是某一个对象--someObject,又有什么不同呢?
3. 对方法加了 synchronized 关键字或用 synchronized(xxx) 包裹了代码,就一定能避免多线程环境下的数据破坏吗?
4. 对方法加 synchronized 关键字与用 synchronized(xxx) 同步代码块两种规避方法又有什么分别和联系呢?
为了理解上面的问题,我们还得从 Java 对线程同步的原理上说起。我们知道 Java 直接在语言级上支持多线程的。在多线程环境中我们要小心的数据是:
1) 保存在堆中的实例变量
2) 保存在方法区中的类变量。
现实点说呢就是某个方法会触及到的同一个变量,如类变量或单态实例的实例变量。避免冲突的最容易想到的办法就是同一时刻只让一个线程去执行某段代码块或方法,于是我们就要给一段代码块或整个方法体标记出来,被保护的代码块或方法体在 Java 里叫做监视区域(Monitor Region),类似的东西在 C++ 中叫做临界区(Critical Section)。
比如说一段代码:
01.public void operate() {
02. flag ++;
03. try {
04. //休眠一个随机时间,让不同线程能在此交替执行
05. Thread.sleep(new Random().nextInt(10));
06. } catch (InterruptedException e) {
07. e.printStackTrace();
08. }
09. flag --;
10. System.out.println("Current flag: " + flag);
11.}