本文僅僅是爲了說明synchronized關鍵字同步的是對象不是方法,列子的確有失偏頗。html
一.明確一點synchronized同步的是對象不是方法也不是代碼塊 java
我有關synchronized同步的是對象討論的博客在這裏:http://www.javashuo.com/article/p-hqlnozga-x.html安全
只要明確了synchronized同步的是對象那麼,底下的問題就好解決了。ide
二.問題的導入函數
首先我有一個班級,班級中有學生。那麼咱們能夠這樣來模擬這個問題,代碼以下:this
import java.util.ArrayList; public class Class { //班級類 ArrayList<Student> students; public Class(ArrayList<Student> students){ this.students=students; } } //學生類 class Student{ private String name; private int ID; public Student(String name,int ID){ this.ID=ID; this.name=name; } }
而後咱們有一個班級事務處理類,來處理事務,這個類繼承了Thread類代碼入下:spa
import java.util.ArrayList; import java.util.Arrays; public class ClassText extends Thread{//班級事務處理類 Class class1;//待處理事務的班級 //構造器 public ClassText(Class class1){ this.class1=class1; } @Override//沒有同步run方法 public void run(){ super.run(); addStudent(new Student("張三",15)); } //添加學生,沒有同步該方法 public void addStudent(Student student){ System.out.println("添加前如今有"+class1.students.size()+"個學生"); class1.students.add(student); System.out.println("添加後如今有"+class1.students.size()+"個學生"); } public static void main(String[] args) { Class c=new Class(new ArrayList<>(Arrays.asList(new Student("李四",20),new Student("趙牛",20)))); ClassText ct1=new ClassText(c); ClassText ct2=new ClassText(c); ct1.start(); ct2.start(); } }
顯然上面的代碼是錯誤的,他沒同步兩個線程,那麼咱們看看結果是什麼樣子的:線程
果真出現了線程不安全的狀況。code
那麼,咱們立刻同步化咱們的addStudent方法或者是run方法,代碼以下:htm
這裏同步addStudent方法
//添加學生,同步化該方法 public synchronized void addStudent(Student student){ System.out.println("添加前如今有"+class1.students.size()+"個學生"); class1.students.add(student); System.out.println("添加後如今有"+class1.students.size()+"個學生"); }
結果以下:
仍是不一樣步的。
那麼這究竟是爲何呢?
三.解決方法
其實咱們不管在ClassText類裏面的哪一個方法加synchronized使其同步化都是沒有用的,由於你同步化的是你的ClassText對象,而咱們要同步的是處理的是在ClassText類中組合Class對象,由於咱們是對他的進行共享資源的操做,那麼問題來了,怎麼對不是自己對象進行一個同步化操做呢?仍是使用synchronized同步代碼塊只不過同步對象再也不是this了而是共享數據處理的對象,修改代碼以下:
//添加學生 public void addStudent(Student student){ //同步化修改共享數據的對象 synchronized (class1){ System.out.println("添加前如今有"+class1.students.size()+"個學生"); class1.students.add(student); System.out.println("添加後如今有"+class1.students.size()+"個學生"); } }
結果以下:沒毛病了!
固然你把添加學生的添加函數放到Class類中,同步改方法也是沒問題的
因此仍是那一條,同步化的對象,是須要同步化的對象。