Java并发编程:线程安全
当多个线程需要同时访问同一个资源(成为临界资源)时,可能会对数据造成破坏。
例如:money += 1000; 当第一个线程取出money值,然后+1000完成这两步之后,第二个进程进入读取了count的值,又+1000 再赋值给count。 这时 第一个线程再进行第三步,把它的值赋给count,这就出现了混乱,没有达到预期的效果(两个线程只存进去了1000)。
所以,需要同步互斥访问来解决线程安全问题。 Java提供了 synchronized 和 Lock 方式进行同步互斥访问。
1.synchronized 关键字
->实例方法同步,对对象加锁
public synchronized void add(int i){ i += 1; }
一个线程只能访问一个实例变量的add()方法
->静态方法同步
public static synchronized void add(int i){ i += 1; }
同步在该方法所在的类对象(JVM中一个类只有一个类对象.class)上,所以同时只允许一个线程执行同一个类中的静态同步方法。
->实例方法中的同步块
public void add(int i){ synchronized(this){ //this指代调用add()方法的实例对象(即监视器对象),一次只有一个线程可以执行该同步代码块 i += 1; //当在某个线程中执行这段代码块,该线程获取这个对象的锁,从而使得其他线程无法同时访问该代码块。 } }
->静态方法中的同步块
public static synchronized void add(int i){ //只能被类对象的一个线程访问 synchronized(this){ i += 1; } }
对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。
下面有两个栗子:
1).多个线程访问共享数据:设计4个线程,其中两个对j增加1,另外两个对j减少1
public class MultiThreadShareData { private int j; public static void main(String[] args) { MultiThreadShareData mts = new MultiThreadShareData(); Inc inc = mts.new Inc(); Dec dec = mts.new Dec(); for(int i=0; i<2; i++){ Thread t1 = new Thread(inc); t1.start(); t1 = new Thread(dec); t1.start(); } } private synchronized void inc(){ j++; System.out.println(Thread.currentThread().getName() + "-inc:" + j); } private synchronized void dec(){ j--; System.out.println(Thread.currentThread().getName() + "-dec:" + j); } class Inc implements Runnable{ public void run() { for(int i=0; i<100; i++){ inc(); } } } class Dec implements Runnable { public void run() { for(int i=0; i<100; i++) { dec(); } } } }
2).设计程序:子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次。
public class ThreadCommunication { public static void main(String[] args) { final Business bussiness = new Business(); new Thread(new Runnable(){ public void run() { for(int i=1; i<=50; i++){ bussiness.sub(i); } } }).start(); for(int i=1; i<=50; i++){ bussiness.main(i); } } } class Business{ private boolean bShouldSub = true; public synchronized void sub(int i) { while(!bShouldSub){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for(int j=1; j<=10; j++) { System.out.println("sub thread sequence of " + j + ", loop of " + i ); } bShouldSub = false; this.notify(); } public synchronized void main(int i) { while(bShouldSub){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for(int j=1; j<=100; j++) { System.out.println("main thread sequence of " + j + ", loop of " + i ); } bShouldSub = true; this.notify(); } }