深入理解Java併發synchronized同步化的程式碼塊不是this物件時的操作
阿新 • • 發佈:2018-11-10
一.明確一點synchronized同步的是物件不是方法也不是程式碼塊
我有關synchronized同步的是物件討論的部落格在這裡:https://www.cnblogs.com/SAM-CJM/p/9798263.html
只要明確了synchronized同步的是物件那麼,底下的問題就好解決了。
二.問題的匯入
首先我有一個班級,班級中有學生。那麼我們可以這樣來模擬這個問題,程式碼如下:
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類程式碼入下:
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(); } }
顯然上面的程式碼是錯誤的,他沒同步兩個執行緒,那麼我們看看結果是什麼樣子的:
果然出現了執行緒不安全的情況。
那麼,我們馬上同步化我們的addStudent方法或者是run方法,程式碼如下:
這裡同步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類中,同步改方法也是沒問題的
所以還是那一條,同步化的物件,是需要同步化的物件。
來源:https://www.cnblogs.com/SAM-CJM/p/9810385.html