Java多線程——線程安全問題

1、什麼狀況下會產生線程安全問題?

同時知足如下兩個條件時:java

1,多個線程在操做共享的數據。
2,操做共享數據的線程代碼有多條。面試

當一個線程在執行操做共享數據的多條代碼過程當中,其餘線程參與了運算,就會致使線程安全問題的產生。設計模式

例1:四個線程賣100張票安全

public class TicketDemo implements Runnable {
	private int tickets = 100;

	public void run() {
		while (true) {

			if (tickets > 0) {
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
				}
				System.out.println(Thread.currentThread().getName() + "....sale:...." + tickets--);
			}
		}
	}

	public static void main(String[] args) {
		TicketDemo ticketDemo = new TicketDemo();
		Thread t1 = new Thread(ticketDemo);
		Thread t2 = new Thread(ticketDemo);
		Thread t3 = new Thread(ticketDemo);
		Thread t4 = new Thread(ticketDemo);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}
Thread-3....sale....100
Thread-2....sale....99
Thread-0....sale....97
Thread-1....sale....98
Thread-3....sale....96
Thread-1....sale....94
Thread-0....sale....94
Thread-2....sale....95
Thread-1....sale....93
Thread-0....sale....92
Thread-2....sale....92
Thread-3....sale....92
Thread-0....sale....91
Thread-2....sale....89
Thread-3....sale....90
Thread-1....sale....91
Thread-1....sale....88
Thread-3....sale....86
Thread-0....sale....88
Thread-2....sale....87
Thread-2....sale....84
Thread-3....sale....84
Thread-1....sale....85
Thread-0....sale....83
Thread-1....sale....82
Thread-0....sale....80
Thread-3....sale....79
Thread-2....sale....81
Thread-3....sale....78
Thread-2....sale....75
Thread-1....sale....76
Thread-0....sale....77
Thread-2....sale....74
Thread-1....sale....71
Thread-0....sale....73
Thread-3....sale....72
Thread-1....sale....70
Thread-0....sale....68
Thread-3....sale....69
Thread-2....sale....67
Thread-2....sale....66
Thread-3....sale....64
Thread-0....sale....63
Thread-1....sale....65
Thread-2....sale....62
Thread-0....sale....62
Thread-1....sale....60
Thread-3....sale....61
Thread-2....sale....59
Thread-0....sale....57
Thread-3....sale....58
Thread-1....sale....59
Thread-0....sale....56
Thread-1....sale....56
Thread-3....sale....55
Thread-2....sale....56
Thread-1....sale....54
Thread-2....sale....54
Thread-0....sale....54
Thread-3....sale....53
Thread-0....sale....52
Thread-3....sale....52
Thread-2....sale....50
Thread-1....sale....51
Thread-2....sale....49
Thread-0....sale....49
Thread-3....sale....48
Thread-1....sale....48
Thread-2....sale....46
Thread-0....sale....44
Thread-3....sale....45
Thread-1....sale....47
Thread-1....sale....43
Thread-0....sale....42
Thread-2....sale....42
Thread-3....sale....41
Thread-1....sale....40
Thread-0....sale....39
Thread-3....sale....39
Thread-2....sale....40
Thread-2....sale....38
Thread-1....sale....37
Thread-3....sale....35
Thread-0....sale....36
Thread-3....sale....34
Thread-1....sale....33
Thread-0....sale....32
Thread-2....sale....31
Thread-3....sale....30
Thread-1....sale....29
Thread-0....sale....29
Thread-2....sale....28
Thread-3....sale....27
Thread-0....sale....25
Thread-1....sale....26
Thread-2....sale....24
Thread-1....sale....23
Thread-0....sale....23
Thread-3....sale....22
Thread-2....sale....21
Thread-1....sale....20
Thread-3....sale....20
Thread-0....sale....20
Thread-2....sale....19
Thread-3....sale....16
Thread-0....sale....17
Thread-1....sale....18
Thread-2....sale....15
Thread-0....sale....13
Thread-1....sale....12
Thread-3....sale....14
Thread-2....sale....11
Thread-3....sale....10
Thread-0....sale....8
Thread-1....sale....9
Thread-2....sale....7
Thread-3....sale....6
Thread-0....sale....5
Thread-1....sale....4
Thread-2....sale....3
Thread-1....sale....2
Thread-3....sale....2
Thread-2....sale....1
Thread-0....sale....2
運行結果

  觀察結果,咱們發現會有多個線程賣到同一張票和賣到0號票的狀況,這就是線程安全問題。多線程


 

解決思路:

將多條操做共享數據的線程代碼封裝起來,當有線程在執行這些代碼的時候,其餘線程不能夠參與運算。ide

當前線程把這些代碼都執行完畢後,其餘線程才能夠參與運算。函數

在java中,用同步代碼塊就能夠解決這個問題。this

同步代碼塊的格式:
synchronized(對象)
{
須要被同步的代碼 ;
}spa

這個對象通常稱爲同步鎖線程

同步的前提:同步中必須有多 個線程並使用同一個鎖。

同步的好處:解決了線程的安全問題。

同步的弊端:相對下降了效率,由於同步外的線程的都會判斷同步鎖。

解決例1的線程安全問題代碼:

public class TicketDemo implements Runnable {
	private int tickets = 100;
	Object obj = new Object();

	public void run() {
		while (true) {
			synchronized (obj) {
				if (tickets > 0) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
					}
					System.out.println(Thread.currentThread().getName() + "....sale...." + tickets--);
				}
			}
		}
	}

	public static void main(String[] args) {
		TicketDemo ticketDemo = new TicketDemo();
		Thread t1 = new Thread(ticketDemo);
		Thread t2 = new Thread(ticketDemo);
		Thread t3 = new Thread(ticketDemo);
		Thread t4 = new Thread(ticketDemo);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

  


2、同步鎖是什麼:

同步函數使用的鎖是 this

靜態的同步函數使用的鎖是該函數所屬 字節碼文件對象 ,能夠用 getClass()方法獲取,也能夠用 當前類名.class  表示。

同步函數和同步代碼塊的區別:

同步函數的鎖是固定的this。

同步代碼塊的鎖是任意的對象。

建議使用同步代碼塊。

 1 class Ticket implements Runnable {
 2     private static int num = 100;
 3     boolean flag = true;
 4 
 5     public void run() {
 6         if (flag)
 7             while (true) {
 8                 synchronized (Ticket.class)//(this.getClass())同步代碼塊
 9                 {
10                     if (num > 0) {
11                         try {
12                             Thread.sleep(10);
13                         } catch (InterruptedException e) {
14                         }
15                         System.out.println(Thread.currentThread().getName() + ".....obj...." + num--);
16                     }
17                 }
18             }
19         else
20             while (true)
21                 this.show();
22     }
23 
24     public static synchronized void show()//同步函數
25     {
26         if (num > 0) {
27             try {
28                 Thread.sleep(10);
29             } catch (InterruptedException e) {
30             }
31             System.out.println(Thread.currentThread().getName() + ".....function...." + num--);
32         }
33     }
34 }
35 
36 class StaticSynFunctionLockDemo {
37     public static void main(String[] args) {
38         Ticket t = new Ticket();
39         
40         Thread t1 = new Thread(t);
41         Thread t2 = new Thread(t);
42 
43         t1.start();
44         try {
45             Thread.sleep(10);
46         } catch (InterruptedException e) {
47         }
48         t.flag = false;
49         t2.start();
50     }
51 }
View Code

 


3、死鎖常見狀況:

同步嵌套時,兩個線程你拿了個人鎖,我拿了你的鎖,都不釋放,形成死鎖。

 

能夠記一套死鎖狀況代碼,面試可能用獲得。

死鎖狀況:

class Testa implements Runnable {
	private boolean flag;
	Testa(boolean flag) {
		this.flag = flag;
	}
	public void run() {
		if (flag) {
			while (true)
				synchronized (MyLock.locka) {
					System.out.println(Thread.currentThread().getName() + "..if   locka....");
					synchronized (MyLock.lockb) {
						System.out.println(Thread.currentThread().getName() + "..if   lockb....");
					}
				}
		} else {
			while (true)
				synchronized (MyLock.lockb) {
					System.out.println(Thread.currentThread().getName() + "..else  lockb....");
					synchronized (MyLock.locka) {
						System.out.println(Thread.currentThread().getName() + "..else   locka....");
					}
				}
		}
	}
}
class MyLock {
	public static final Object locka = new Object();
	public static final Object lockb = new Object();
}
class DeadLockTest {
	public static void main(String[] args) {
		Testa a = new Testa(true);
		Testa b = new Testa(false);
		Thread t1 = new Thread(a);
		Thread t2 = new Thread(b);
		t1.start();
		t2.start();
	}
}

  


4、單例設計模式中的線程安全問題

//餓漢式
class Single
{
	private static final Single s = new Single();
	private Single(){}
	public static Single getInstance()
	{
		return s;
	}
}

//懶漢式
/*
*加入同步是爲了解決多線程安全問題。
*
*加入雙重判斷不用每次都判斷是否上鎖,是爲了解決效率問題。
**/

class Single
{
	private static Single s = null;
	private Single(){}
	public static Single getInstance()
	{
		if(s==null)
		{
			synchronized(Single.class)		
			{
				if(s==null)
		//				-->0 -->1
					s = new Single();
			}
		}
		return s;
	}
}

開發用餓漢式,沒有線程安全問題。——餓漢式在類建立的同時就已經建立好一個靜態的對象供系統使用,之後再也不改變,因此天生是線程安全的。

面試懶漢式,記住如何解決線程安全問題。

相關文章
相關標籤/搜索