并发编程-同步机制(一)
一、synchronized 內置鎖
java 關鍵字synchronized 用于保證線程對變量訪問的可見性與排他性,又可以稱之為內置鎖機制。
synchronized 可以修飾方法跟同步塊上來進行使用,確保多個線程只有一個線程處于方法或者同步塊中。
同步方法與同步塊
使用 前面學習過的CountDownLatch 做并發測試
import java.util.concurrent.CountDownLatch;/*** 演示synchronized 同步方法,同步塊基本使用方法* * @author ckj**/ public class SynchronizedTest {static CountDownLatch latch = new CountDownLatch(10);private Object obj = new Object();private int num = 0;/*** 同步方法加鎖*/public synchronized void synIncNum() {num++;}/*** 同步塊加鎖*/public void synIncNum2() {synchronized(this) {num++;}//也可以寫成這樣 // synchronized(obj) { // num++; // }}public void incNum() {num++;}static class MyThread extends Thread {private SynchronizedTest syn;public MyThread(SynchronizedTest syn) {this.syn = syn;}public void run() {for (int i = 0; i < 10000; i++) {//幾個測試方法自己手動去切換測試syn.incNum();//syn.synIncNum();//syn.synIncNum2();}latch.countDown();};}public static void main(String[] args) throws InterruptedException {SynchronizedTest syn = new SynchronizedTest();//啟動10個線程滿足latch 計數器for(int i =0 ;i<10;i++) {new MyThread(syn).start();}latch.await();System.out.println(syn.num); //理想值應該是100000}}從測試類的測試結果可以看的出來,如果沒有加同步機制在多線程的情況下會出現并發問題。
那么synchronized 到底鎖的是什么呢。 從上面的例子看來synchronized 鎖的是其實就是對象
其實你可以認為在方法上使用synchronized關鍵字其實他的含義就是synchronized(this) 。從這個方向去理解synchronized鎖的話那么synchronized鎖不同對象的話那么線程就可以并行了。
對象鎖
/*** 演示鎖的對象不同線程并行* * @author ckj**/ public class InstanceSynTest {static class MyThread implements Runnable {private InstanceSynTest syn;public MyThread(InstanceSynTest syn) {this.syn = syn;}public void run() {System.out.println("開始執行MyThread" + Thread.currentThread().getName());syn.syncInstance1();}}static class MyThread2 implements Runnable {private InstanceSynTest syn;public MyThread2(InstanceSynTest syn) {this.syn = syn;}public void run() {System.out.println("開始執行MyThread2" + Thread.currentThread().getName());syn.syncInstance2();}}public synchronized void syncInstance1() {try {Thread.sleep(2000);System.out.println("syncInstance1 開始執行..." + this.toString());Thread.sleep(2000);System.out.println("syncInstance1 結束執行 " + this.toString());} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public synchronized void syncInstance2() {try {Thread.sleep(2000);System.out.println("synInstance2 開始執行..." + this.toString());Thread.sleep(2000);System.out.println("synInstance2 結束執行 " + this.toString());} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public static void main(String[] args) throws InterruptedException {InstanceSynTest ins1 = new InstanceSynTest();InstanceSynTest ins2 = new InstanceSynTest();Thread thread1 = new Thread(new MyThread(ins1));Thread thread2 = new Thread(new MyThread2(ins2)); // 由于傳入的對象是不同對象,線程執行的順序是并行的//Thread thread2 = new Thread(new MyThread2(ins1)); // 修改為同一對象的話那么,線程執行變為串行thread1.start();thread2.start();Thread.sleep(6000);} }如果我我們把鎖加在static 方法上面呢,那么又會是說明結果。我們知道類的對象可以有許多,但是類只有一個class 對象只有一個,所以不同對象實例的對象鎖是是互不干擾的,但是每個類都只有一個類鎖。但是有一點類鎖只是概念上的東西。
類鎖
稍微修改上面測試類
/*** 演示實例鎖和類鎖的不同,可以并行開始 * * @author ckj**/ public class InstanceClassSynTest {static class MyThread implements Runnable {private InstanceClassSynTest syn;public MyThread(InstanceClassSynTest syn) {this.syn = syn;}public void run() {System.out.println("開始執行MyThread" + Thread.currentThread().getName());syn.syncInstance1();}}static class MyThread2 implements Runnable {public void run() {System.out.println("開始執行MyThread2" + Thread.currentThread().getName());syncInstance2();}}public synchronized void syncInstance1() {try {Thread.sleep(2000);System.out.println("syncInstance1 開始執行..." + this.toString());Thread.sleep(2000);System.out.println("syncInstance1 結束執行 " + this.toString());} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public static synchronized void syncInstance2() {try {Thread.sleep(2000);System.out.println("synInstance2 開始執行...");Thread.sleep(2000);System.out.println("synInstance2 結束執行 " );} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public static void main(String[] args) throws InterruptedException {InstanceClassSynTest ins1 = new InstanceClassSynTest();Thread thread1 = new Thread(new MyThread(ins1));Thread thread2 = new Thread(new MyThread2()); // 由于傳入的對象是不同對象,線程執行的順序是并行的thread1.start();thread2.start();Thread.sleep(6000);} }下面演示一個比較經典的錯誤加鎖的案例
/*** 演示錯誤加鎖* * @author ckj**/ public class IntegerSynTest {static class MyThread implements Runnable {private Integer i;public MyThread(Integer i) {this.i = i;}public void run() {synchronized (i) {Thread thread = Thread.currentThread();i++;System.out.println(i);try {thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}public static void main(String[] args) {MyThread myThread = new MyThread(1);for(int i = 0;i<5;i++) {new Thread(myThread).start();}}}按照我們預期結果的話輸入應該是
2
3
4
5
6
結果應該如上是2->3->4->5->6
實際結果是:
4
6
5
4
4
那哪里出問題了呢
增加日記打印
public void run() {synchronized (i) {Thread thread = Thread.currentThread();i++;System.out.println(thread.getName()+"-------"+i+"-@"+System.identityHashCode(i)+"----------"+"-@"+System.identityHashCode(this));try {thread.sleep(1000);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}System.identityHashCode 方法打印出原生Object 的hashCode 可以認為他是內存地址
Thread-1-------3-@1073512853-----------@825980954
Thread-2-------4-@923214033-----------@825980954
Thread-3-------5-@1428845299-----------@825980954
Thread-4-------6-@1534375319-----------@825980954
Thread-0-------3-@1073512853-----------@825980954
發現了原來 i 的對象一直在變,每次鎖的對象都發生的變化當然得不到預期結果咯,那么為什么會這樣呢。通過反編譯工具打開class 類
發現他調用了Integer.valueof 方法,查看一下
public static Integer valueOf(String s, int radix) throws NumberFormatException {return Integer.valueOf(parseInt(s,radix));}public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}他重新new 了一個出來,所以鎖不住了。
那我們怎么處理呢?上面打印結果就可以看出了,我們直接鎖this 對象就可以解決問題了。
public void run() {synchronized (this) {Thread thread = Thread.currentThread();i++;System.out.println(thread.getName()+"-------"+i+"-@"+System.identityHashCode(i)+"----------"+"-@"+System.identityHashCode(this));try {thread.sleep(1000);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
?
總結
以上是生活随笔為你收集整理的并发编程-同步机制(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaScript代码规范(CKJ)
- 下一篇: Chandy-Lamport分布式快照算