
線程安全
線程安全就是多線程訪問時,采用了加鎖機制,當一個線程訪問該類的某個數(shù)據(jù)時,進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用。不會出現(xiàn)數(shù)據(jù)不一致或者數(shù)據(jù)污染。
線程不安全就是不提供數(shù)據(jù)訪問保護,有可能出現(xiàn)多個線程先后更改數(shù)據(jù)造成所得到的數(shù)據(jù)是臟數(shù)據(jù)。
概述
如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。
或者說:一個類或者程序所提供的接口對于線程來說是原子操作或者多個線程之間的切換不會導致該接口的執(zhí)行結果存在二義性,也就是說我們不用考慮同步的問題。
線程安全問題都是由全局變量及靜態(tài)變量引起的。
若每個線程中對全局變量、靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執(zhí)行寫操作,一般都需要考慮線程同步,否則的話就可能影響線程安全。
如何保證線程安全:
1.把共享的變量數(shù)據(jù)標識為private
2.使用synchronized關鍵字同步方法或代碼。
volidate關鍵字的使用
volatile是一個類型 修飾符 (type specifier)。它是被設計用來修飾被不同線程訪問和修改的 變量 。如果沒有volatile,基本上會導致這樣的結果:要么無法編寫多線程程序,要么 編譯器 失去大量優(yōu)化的機會。
Volidate 和Synchroinzed 區(qū)別
java中使用Volidate變量所需的編碼較少,并且運行時開銷也較少,但是它所能實現(xiàn)的功能也僅是 synchronized 的一部分。Synchronized是對Volidate的基礎上增加了互斥的功能。
1.Volidate:只保證可見性,可以多個線程同時訪問voliadte修飾的變量。
2.Synchroinzed:既保證了可見性又保證了互斥性。同時只能有一個線程去訪問。 volatile 變量可以被看作是一種 “程度較輕的 synchronized”;與 synchronized 塊相比,volatile 與鎖相比,Volatile 變量是一種非常簡單但同時又非常脆弱的同步機制,它在某些情況下將提供優(yōu)于鎖的性能和伸縮性。如果嚴格遵循 volatile 的使用條件 —— 即變量真正獨立于其他變量和自己以前的值 —— 在某些情況下可以使用 volatile 代替 synchronized 來簡化代碼。然而,使用 volatile 的代碼往往比使用鎖的代碼更加容易出錯。
當使用volatile 聲明的變量的值的時候,系統(tǒng)總是重新從它所在的內存讀取數(shù)據(jù),即使它前面的指令剛剛從該處讀取過數(shù)據(jù)。而且讀取的數(shù)據(jù)立刻被保存。
經典賣票的例子
public class Multithread implements Runnable
{
//總票數(shù)
private int ticket = 100;
@Override
public void run()
{
saleTicket();
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public void saleTicket()
{
while (true)
{
if (ticket > 0)
{
System.out.println(Thread.currentThread().getName() + " 正在賣第" + ticket + "張票");
ticket--;
}
else
{
System.out.println("余票不足!");
break;
}
}
}
public static void main(String[] args)
{
Multithread mt = new Multithread();
new Thread(mt, "窗口1").start();
new Thread(mt, "窗口2").start();
new Thread(mt, "窗口3").start();
new Thread(mt, "窗口4").start();
}
}
利用Synchroinzed 關鍵字進行同步鎖
public class Multithread implements Runnable
{
//總票數(shù)
private int ticket = 100;
//同步鎖對象
Object obj = new Object();
/**
* 利用synchronized關鍵字對代碼塊進行同步
* 保證同一時間只有一個線程在操作共享變量 其余線程則等待該線程執(zhí)行完畢后再執(zhí)行
* obj 對象鎖可以是任意類型
* 注意: 要保證對象鎖是同一個
*/
@Override
public void run()
{
saleTicket();
try
{
//線程睡眠10毫秒 每個線程都有執(zhí)行機會
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public void saleTicket()
{
while (true)
{
synchronized (obj){if (ticket > 0)
{
System.out.println(Thread.currentThread().getName() + " 正在賣第" + ticket + "張票");
ticket--;
}
else
{
System.out.println("余票不足!");
break;
}
}
}
}
public static void main(String[] args)
{
Multithread mt = new Multithread();
new Thread(mt, "窗口1").start();
new Thread(mt, "窗口2").start();
new Thread(mt, "窗口3").start();
new Thread(mt, "窗口4").start();
}
}
通過synchronized關鍵字對代碼塊進行同步(加鎖),保證共享的變量同一時間只能有一個線程在操作。其他線程則等待該線程執(zhí)行完畢后在進行操作,這樣就保證了線程的同步和共享數(shù)據(jù)的安全!
注意
synchronized 同步代碼塊的鎖可以是任意類對象(繼承Thread類),這個對象必須是線程共享類(靜態(tài)的)
synchronized 可以加在方法上,如果是靜態(tài)方法synchronized的鎖就是這個類的類對象,如果不是靜態(tài)方法,synchronized 加在對象方法上,這個鎖就是this