- 線程安全
如果有多個線程在同時運行,而這些線程可能會同時運行這段代碼。程序每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。
我們通過一個案例,演示線程的安全問題:
電影院要賣票,我們模擬電影院的賣票過程。假設要播放的電影是 “功夫熊貓3”,本次電影的座位共100個(本場電影只能賣100張票)。
我們來模擬電影院的售票窗口,實現多個窗口同時賣 “功夫熊貓3”這場電影票(多個窗口一起賣這100張票)
需要窗口,採用線程對象來模擬;需要票,Runnable接口子類來模擬
測試類
public class ThreadDemo {
public static void main(String[] args) {
//創建票對象
Ticket ticket = new Ticket();
//創建3個窗口
Thread t1 = new Thread(ticket, "窗口1");
Thread t2 = new Thread(ticket, "窗口2");
Thread t3 = new Thread(ticket, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
- 模擬票
public class Ticket implements Runnable {
//共100票
int ticket = 100;
@Override
public void run() {
//模擬賣票
while(true){
if (ticket > 0) {
//模擬選坐的操作
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在賣票:" + ticket--);
}
}
}
}
運行結果發現:上面程序出現了問題
票出現了重複的票
錯誤的票 0、-1
其實,線程安全問題都是由全局變量及靜態變量引起的。若每個線程中對全局變量、靜態變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線程同步,否則的話就可能影響線程安全。
2.線程同步(線程安全處理Synchronized)
java中提供了線程同步機制,它能夠解決上述的線程安全問題。
線程同步的方式有兩種:
- 方式1:同步代碼塊
- 方式2:同步方法
同步代碼塊
同步代碼塊: 在代碼塊聲明上 加上synchronized
synchronized (鎖對象) {
可能會產生線程安全問題的代碼
}
同步代碼塊中的鎖對象可以是任意的對象;但多個線程時,要使用同一個鎖對象才能夠保證線程安全。
使用同步代碼塊,對電影院賣票案例中Ticket類進行如下代碼修改:
public class Ticket implements Runnable {
//共100票
int ticket = 100;
//定義鎖對象
Object lock = new Object();
@Override
public void run() {
//模擬賣票
while(true){
//同步代碼塊
synchronized (lock){
if (ticket > 0) {
//模擬電影選坐的操作
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在賣票:" + ticket--);
}
}
}
}
}
當使用了同步代碼塊後,上述的線程的安全問題,解決了。
同步方法
- 同步方法:在方法聲明上加上synchronized
public synchronized void method(){
可能會產生線程安全問題的代碼
}
同步方法中的鎖對象是 this
使用同步方法,對電影院賣票案例中Ticket類進行如下代碼修改:
public class Ticket implements Runnable {
//共100票
int ticket = 100;
//定義鎖對象
Object lock = new Object();
@Override
public void run() {
//模擬賣票
while(true){
//同步方法
method();
}
}
//同步方法,鎖對象this
public synchronized void method(){
if (ticket > 0) {
//模擬選坐的操作
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在賣票:" + ticket--);
}
}
}
- 靜態同步方法: 在方法聲明上加上static synchronized
public static synchronized void method(){
可能會產生線程安全問題的代碼
}
靜態同步方法中的鎖對象是 類名.class