併發編程基礎(第一講)-----synchronized 使用理解

線程安全的概念:

當多個線程訪問某個類(對象或者方法)時,這個類始終都能表現出正確的行爲,那麼這個類就是線程安全

synchronized:可以在任意對象以及方法上加鎖,而加鎖的這段代碼稱爲互斥去或者臨界區

package mythread;

/**
 * 線程安全概念:當多個線程訪問某一個類(對象或方法)時,這個對象始終都能表現出正確的行爲,那麼這個類(對象或方法)就是線程安全的。
 * synchronized:可以在任意對象及方法上加鎖,而加鎖的這段代碼稱爲"互斥區"或"臨界區"
 * @author alienware
 *
 */
public class MyThread extends Thread{
	
	private int count = 5 ;
	
	//synchronized加鎖
	public synchronized void run(){
		count--;
		System.out.println(this.currentThread().getName() + " count = "+ count);
	}
	
	public static void main(String[] args) {
		/**
		 * 分析:當多個線程訪問myThread的run方法時,以排隊的方式進行處理(這裏排對是按照CPU分配的先後順序而定的),
		 * 		一個線程想要執行synchronized修飾的方法裏的代碼:
		 * 		1 嘗試獲得鎖
		 * 		2 如果拿到鎖,執行synchronized代碼體內容;拿不到鎖,這個線程就會不斷的嘗試獲得這把鎖,直到拿到爲止,
		 * 		   而且是多個線程同時去競爭這把鎖。(也就是會有鎖競爭的問題)
		 */
		MyThread myThread = new MyThread();
		Thread t1 = new Thread(myThread,"t1");
		Thread t2 = new Thread(myThread,"t2");
		Thread t3 = new Thread(myThread,"t3");
		Thread t4 = new Thread(myThread,"t4");
		Thread t5 = new Thread(myThread,"t5");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
	}
}

-----> 當多個線程訪問my Thread的run方法時,以排隊的方法進行處理,這裏排隊是按照CPU分配的先後順序而定的,一個線程想要執行synchronized修飾的方法裏的代碼,首先是嘗試獲得鎖,如果拿到鎖。,執行synchronized代碼體內容,拿不到鎖,這個線程就會不斷的嘗試獲得這把鎖,直到拿到爲止,而且是多個線程同時去競爭這把鎖。

 

多個線程多個鎖

 

package mythread;
/**
 * 關鍵字synchronized取得的鎖都是對象鎖,而不是把一段代碼(方法)當做鎖,
 * 所以代碼中哪個線程先執行synchronized關鍵字的方法,哪個線程就持有該方法所屬對象的鎖(Lock),
 * 
 * 在靜態方法上加synchronized關鍵字,表示鎖定.class類,類一級別的鎖(獨佔.class類)。
 * @author alienware
 *
 */
public class MultiThread {

	private int num = 0;
	
	/** static */
	public synchronized void printNum(String tag){
		try {
			
			if(tag.equals("a")){
				num = 100;
				System.out.println("tag a, set num over!");
				Thread.sleep(1000);
			} else {
				num = 200;
				System.out.println("tag b, set num over!");
			}
			
			System.out.println("tag " + tag + ", num = " + num);
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	//注意觀察run方法輸出順序
	public static void main(String[] args) {
		
		//倆個不同的對象
		final MultiThread m1 = new MultiThread();
		final MultiThread m2 = new MultiThread();
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				m1.printNum("a");
			}
		});
		
		Thread t2 = new Thread(new Runnable() {
			@Override 
			public void run() {
				m2.printNum("b");
			}
		});		
		
		t1.start();
		t2.start();
		
	}
	
	
	
	
}

 

對象鎖的同步和異步

同步:syschronized

     同步的慨念就是共享,我們要牢牢記住共享着兩個字,如果不是共享的資源就沒必要進行工程

異步:asychronized

   異步的慨念就是獨立,互相之前不收到任何制約,就好像我們學習http的時候,在頁面發起的Ajax請求,我們還可以繼續瀏覽或操作頁面的內容,二者之間沒有任何關係

同步的目的就是爲了線程安全,其實對於線程安全來說,需要滿足兩個特性

    原子性

   可見性

package mythread;

/**
 * 對象鎖的同步和異步問題
 * @author alienware
 *
 */
public class MyObject {

	public synchronized void method1(){
		try {
			System.out.println(Thread.currentThread().getName());
			Thread.sleep(4000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/** synchronized */
	public void method2(){
			System.out.println(Thread.currentThread().getName());
	}
	
	public static void main(String[] args) {
		
		final MyObject mo = new MyObject();
		
		/**
		 * 分析:
		 * t1線程先持有object對象的Lock鎖,t2線程可以以異步的方式調用對象中的非synchronized修飾的方法
		 * t1線程先持有object對象的Lock鎖,t2線程如果在這個時候調用對象中的同步(synchronized)方法則需等待,也就是同步
		 */
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				mo.method1();
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				mo.method2();
			}
		},"t2");
		
		t1.start();
		t2.start();
		
	}
	
}

髒讀

 

 

package mythread;
/**
 * 業務整體需要使用完整的synchronized,保持業務的原子性。
 * @author alienware
 *
 */
public class DirtyRead {

	private String username = "wkn";
	private String password = "123";
	
	public synchronized void setValue(String username, String password){
		this.username = username;
		
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		this.password = password;
		
		System.out.println("setValue最終結果:username = " + username + " , password = " + password);
	}
	//這個時候如果在設置的時候有線程調用這個方法會出現藏獨的問題,解決的方法時加synchronized
	public void getValue(){
		System.out.println("getValue方法得到:username = " + this.username + " , password = " + this.password);
	}
	
	
	public static void main(String[] args) throws Exception{
		final DirtyRead dr = new DirtyRead();
//		Thread t2 = new Thread(new Runnable() {
//			@Override
//			public void run() {
//				dr.getValue();
//			}
//		});

		

		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				dr.setValue("z3", "456");		
			}
		});
		t1.start();
		Thread.sleep(1000);
		
		dr.getValue();
	}
	
	
	
}

 

 

 

 

syschronized其他概念

 

package sync005;
/**
 * synchronized的重入
 * @author wkn
 *
 */
public class SyncDubbo1 {

	public synchronized void method1(){
		System.out.println("method1..");
		method2();
	}
	public synchronized void method2(){
		System.out.println("method2..");
		method3();
	}
	public synchronized void method3(){
		System.out.println("method3..");
	}
	
	public static void main(String[] args) {
		final SyncDubbo1 sd = new SyncDubbo1();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				sd.method1();
			}
		});
		t1.start();
	}
}

 

 

 

package sync005;
/**
 * synchronized的重入
 * @author wkn
 *
 */
public class SyncDubbo2 {

	static class Main {
		public int i = 10;
		public synchronized void operationSup(){
			try {
				i--;
				System.out.println("Main print i = " + i);
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	static class Sub extends Main {
		public synchronized void operationSub(){
			try {
				while(i > 0) {
					i--;
					System.out.println("Sub print i = " + i);
					Thread.sleep(100);		
					this.operationSup();
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				Sub sub = new Sub();
				sub.operationSub();
			}
		});
		
		t1.start();
	}
	
	
}

 

 

 

 

 

package com.bjsxt.base.sync006;

/**
 * 使用synchronized代碼塊加鎖,比較靈活
 * @author alienware
 *
 */
public class ObjectLock {

	public void method1(){
		synchronized (this) {	//對象鎖
			try {
				System.out.println("do method1..");
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void method2(){		//類鎖
		synchronized (ObjectLock.class) {
			try {
				System.out.println("do method2..");
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	private Object lock = new Object();
	public void method3(){		//任何對象鎖
		synchronized (lock) {
			try {
				System.out.println("do method3..");
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	
	public static void main(String[] args) {
		
		final ObjectLock objLock = new ObjectLock();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				objLock.method1();
			}
		});
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				objLock.method2();
			}
		});
		Thread t3 = new Thread(new Runnable() {
			@Override
			public void run() {
				objLock.method3();
			}
		});
		
		t1.start();
		t2.start();
		t3.start();
		
		
	}
	
}

 

//不要將字符串常量作爲鎖,因爲string在線程池中的引用只有一個,會造成死循環

package sync006;
/**
 * synchronized代碼塊對字符串的鎖,注意String常量池的緩存功能
 * @author wkn
 *
 */
public class StringLock {

	public void method() {
		//new String("字符串常量")
		synchronized ("字符串常量") {
			try {
				while(true){
					System.out.println("當前線程 : "  + Thread.currentThread().getName() + "開始");
					Thread.sleep(1000);		
					System.out.println("當前線程 : "  + Thread.currentThread().getName() + "結束");
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		final StringLock stringLock = new StringLock();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				stringLock.method();
			}
		},"t1");
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				stringLock.method();
			}
		},"t2");
		
		t1.start();
		t2.start();
	}
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章