java併發編程總結一(待補充)

synchronized關鍵字

對某個對象進行加鎖
使用synchronized關鍵字有以下三種使用方式:

  1. 同步代碼塊
    同步代碼塊鎖的是括號裏面的配置對象
  2. 同步方法
    同步方法鎖的是當前實例對象
  3. 靜態同步方法
    靜態同步方法鎖的是當前類的Class對象
  • 自己新建一個對象作爲鎖住的對象
public class T{
	private int count = 10;
	private Object o =new Object();
	public void m(){
		synchronized(o){
			count--;
			System.out.println(Thtread.currentThread().getName() + "count = " + count);
		}
	}
}
  • 使用this對象作爲鎖對象

public class T{
	private int count = 10;
	public void m(){
		synchronized(this){//同步代碼塊
			count--;
			System.out.println(Thtread.currentThread().getName() + "count = " + count);
		}
	}
}
  • 上文代碼的另外一種寫法
public class T{
	private int count = 10;
	public synchronized void m(){//同步方法
		count--;
		System.out.println(Thtread.currentThread().getName() + "count = " + count);
	}
}
  • 靜態同步方法:沒有this對象的靜態方法如何獲得鎖對象
public class T{
	private int count = 10;
	public synchronized void m(){
		count--;
		System.out.println(Thtread.currentThread().getName() + "count = " + count);
	}
	public static void mm(){
		synchronized(T.class){//靜態的方法沒有this對象,所以用反射來獲取它的鎖對象
			count--;
		}
	}
}
public class T implements Runnable{
	private int count = 10;
	public /*synchronized*/ void run(){//synchronized所標註的代碼塊具備原子性,執行期間不可再分
		count--;
		System.out.println(Thtread.currentThread().getName() + "count = " + count);
	}
	public void main(){
		T t = new T();//只有一個對象
		for(int i = 0; i < 5; i++){
			new Thread(t, "THREAD" + i).start();//開啓五個線程,共同訪問t對象,共同訪問count
		}
	}
}

同步方法和非同步方法是否可以同時被調用

在一個同步方法執行的過程中,非同步的方法可以運行,因爲非同步方法的執行不需要鎖

public class demo6 {
	public synchronized void m1() {
		System.out.println(Thread.currentThread().getName() + "m1 start.... ");
		try {
			Thread.sleep(10000);
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "m1 end.... ");
		
	}
	
	public void m2() {
		try {
			Thread.sleep(5000);
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "m2 .... ");
	}
	
	public static void main(String[] args) {
		demo6 t = new demo6();
		
		new Thread(()->t.m1(), "t1").start();
		new Thread(()->t.m2(), "t2").start();

	}
}	

髒讀

寫時需要加鎖,讀時其實也需要加鎖,否則容易出現髒讀的現象

/*寫的時候加鎖
 * 讀的時候不加鎖
 * 髒讀的現象
 */
package concurrent1;
import java.util.concurrent.TimeUnit;
public class demo8 {
	String name;
	double balance;
	
	public synchronized void set(String name, double balance) {
		this.name = name;
		
		try {
			Thread.sleep(2000);
		}catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		
		this.balance = balance;
	}
	
	public double getBalance(String name) {
		return this.balance;
	}
	
	public static void main(String[] args) {
		demo8 aDemo8 = new demo8();
		
		new Thread(()->aDemo8.set("zhangsan", 100.0)).start();
		
		try {
			TimeUnit.SECONDS.sleep(1);
		}catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		
		System.out.println(aDemo8.getBalance("zhangsan"));
		
		try {
			TimeUnit.SECONDS.sleep(2);
		}catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		System.out.println(aDemo8.getBalance("zhangsan"));
	}
}

可重入

import java.util.concurrent.TimeUnit;

/*
 * 一個同步方法是否可以調用另外一個同步的方法
 * 一個已經擁有某個對象鎖的線程,再次申請的時候仍然會得到該對象的鎖
 * 即:synchronized獲得的鎖是可以重入的
 */
public class demo9 {
	synchronized void m1() {
		System.out.println("m1 start...");
		try {
			TimeUnit.SECONDS.sleep(1);
		}catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		m2();
	}
	
	synchronized void m2() {
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		System.out.println("m2");
	}
}

  • 子類中調用父類的同步方法
//子類中調用父類的同步方法
import java.util.concurrent.TimeUnit;

public class demo10 {
	synchronized void m() {
		System.out.println("m start  ..");
		try {
			TimeUnit.SECONDS.sleep(1);
		}catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		System.out.println("m end");
	}
	
	public static void main(String[] args) {
		new TT().m();
	}
}

class TT extends demo10{
	@Override
	synchronized void m() {
		System.out.println("child m start");
		super.m();
		System.out.println("child m end");
	}
}

程序在執行過程中出現異常,鎖會被釋放

import java.sql.Time;
import java.util.concurrent.TimeUnit;

/*
 * 程序在執行時,如果出現異常,默認情況下鎖會被釋放,這時其他的線程訪問的數據可能會出現不一致的情況
 */
public class T {
	int count = 0;
	synchronized void m() {
		System.out.println(Thread.currentThread().getName() + "  start ");
		while(true) {
			count++;
			System.out.println(Thread.currentThread().getName() + "count = " + count);
			try {
				TimeUnit.SECONDS.sleep(1);
			}catch (InterruptedException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
			
			if(count == 5) {
				int i = 1/0;
			}
		}
	}
	
	public static void main(String[] args) {
		T t =new T();
		
		Runnable r = new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				t.m();
			}
		};
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();			
		}
		new Thread(r, "t2").start();
	}
}

volatile 關鍵字:內存可見性

使一個變量在多個線程間可見。

Java編程語言允許線程訪問共享變量,爲了確保共享變量能夠準確一致地更新,線程確保通過排他鎖單獨獲得這個變量。

Java中的volatile關鍵字就是這個定義的體現。如果一個變量被聲明爲volatile,那麼確保這個變量是“可見的”。可見性的意思是當線程修改一個共享變量的時候,另外一個線程能夠讀到這個修改的值。

具體原理參見:這個

例子:

public class T {
	volatile boolean running = true;
	void m() {
		System.out.println(" m start ");
		while(running) {
			
		}
		System.out.println("m end ...");
	}
	
	public static void main(String[] args) {
		T t =new T();
		
		new Thread(t::m, "t1").start();
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();			
		}
		t.running = false;
	}
}

volatile 與synchronized的區別:

volatile只能保證可見性
synchronized既保證可見性又保證了原子性

public class T {
	volatile int count = 0;//只保證可見性
	void m() {
		for(int i = 0; i < 10000; i++) count++;
	}
	
	public static void main(String[] args) {
		T t =new T();
		
		List<Thread> threads = new ArrayList<Thread>();
		
		for(int i = 0; i < 10; i++)
			threads.add(new Thread(t::m, "thread-" + i));
		
		threads.forEach((o)->o.start());
		
		threads.forEach((o)->{
			try {
				o.join();
			} catch (InterruptedException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		});
		
		System.out.println(t.count);
	}
}

使用Atomic***類來保證原子性

public class T {
	AtomicInteger count = new AtomicInteger(0);
	void m() {
		for(int i = 0; i < 10000; i++){
//			if(count.get() < 1000)  //使用Atomicxxx的多個方法並不具備原子性
			count.incrementAndGet();//用於替代count++的
		}
	}
	
	public static void main(String[] args) {
		T t = new T();
		
		List<Thread> threads = new ArrayList<Thread>();
		
		for (int i = 0; i < 10; i++) {
			threads.add(new Thread(t::m, "thread-" + i));
		}
		
		threads.forEach((o) -> o.start());
		
		threads.forEach((o) -> {
			try {
				o.join();
			} catch (InterruptedException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		});
		System.out.println(t.count);
	}
	
}

synchronized的優化

public class T {
	int count = 0;
	synchronized void m1() {
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		
		count++;
		
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
	
	void m2() {
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		//業務邏輯中只有下面的這句話需要同步,這時不應該在給整個方法加上鎖
		//採用更加細粒度的方法,可以使線程爭用的時間變短,提高效率
		synchronized (this) {
			count++;
		}
		
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}	
}

鎖住的對象屬性發生改變不會影響鎖的使用,但是如果引用的對象改變情況就大爲不同了

public class T {
	Object o = new Object();
	
	void m() {
		synchronized (o) {
			while(true) {
				try {
					TimeUnit.SECONDS.sleep(1);
				} catch (Exception e) {
					// TODO: handle exception
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName());
			}
		}
	}
	
	public static void main(String[] args) {
		T t = new T();
		
		new Thread(t::m, "t1").start();
		
		try {
			TimeUnit.SECONDS.sleep(3);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		
		Thread t2 = new Thread(t::m, "t2");
		
		t.o = new Object();
		
		t2.start();
	}
}

不應該用字符串作爲鎖定的對象

public class T {

	String s1 = "Hello";
	String s2 = "Hello";
	
	void m1() {
		synchronized (s1) {
			
		}
	}
	
	void m2() {
		synchronized (s2) {
			
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章