執行緒同步
執行緒安全
要保證執行緒安全有兩個前提:
- 程式呼叫了多執行緒,
- 多個執行緒操作共同的變數
以上兩個條件滿足后,程式就有可能觸犯執行緒不安全的問題
什么是執行緒不安全?
舉例說明:假如一場演唱會需要售賣門票,有三個售票口,A,B,C,它們會同時售票,假如一共只有100張票,那么當100張票售賣完后,售票口就需要停止作業
以下是是實作代碼
public class SaleTicket extends Thread {
//統計剩余票數
private int countticket = 100;
@Override
public void run() {
while (true) {
if (countticket <= 0) {
break;
} else {
countticket--;
System.out.println(getName() + "售票成功,剩余票數:" + countticket);
}
}
}
}
class Test {
public static void main(String[] args) {
SaleTicket s1 = new SaleTicket();
SaleTicket s2 = new SaleTicket();
SaleTicket s3 = new SaleTicket();
s1.setName("視窗A");
s2.setName("視窗B");
s3.setName("視窗C");
//啟用三條執行緒
s1.start();
s2.start();
s3.start();
}
}
運行結果如下:

可以發現結果出現了重復的資料,這是由于這三個執行緒一直在反復搶占CPU的執行權導致的,解決辦法有三個:
1. synchronized()
同步代碼塊
synchronized(任意物件) {
多條陳述句操作共享資料的代碼
}
此代碼塊的功能是鎖住要執行的代碼塊,代碼鎖住后,其他執行緒即使搶到CPU的執行權,也不能進入鎖定代碼塊進行執行,也就是說,同一時間鎖里面只能有一條執行緒在操作,操作完后解鎖,
修改后代碼:
public class SaleTicket implements Runnable {
//統計剩余票數
private int countTicket = 100;
//給代碼加鎖,同步代碼塊的鎖物件可以是任何物件,但必須唯一
private Object obj= new Object();
@Override
public void run() {
while (true) {
synchronized (obj){
if (countTicket <= 0) {
break;
} else {
countTicket--;
System.out.println(Thread.currentThread().getName() + "售票成功,剩余票數:" + countTicket);
}
}
}
}
}
class Test {
public static void main(String[] args) {
SaleTicket s1 = new SaleTicket();
Thread t1 = new Thread(s1);
Thread t2 = new Thread(s1);
Thread t3 = new Thread(s1);
t1.setName("視窗A");
t2.setName("視窗B");
t3.setName("視窗C");
//啟用三條執行緒
t1.start();
t2.start();
t3.start();
}
}
結果如下:

2. 同步方法
修飾符 synchronized 回傳值型別 方法名(方法引數) {
方法體;
}
將要鎖住的方法提取到方法中,并用synchronized修飾
public class SaleTicket implements Runnable {
//統計剩余票數
private int countTicket = 100;
private Object obj = new Object();
@Override
public void run() {
while (true) {
boolean result = syMethod();
if (result) {
break;
}
}
}
//同步方法
private synchronized boolean syMethod() {
if (countTicket <= 0) {
return true;
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
countTicket--;
System.out.println(Thread.currentThread().getName() + "售票成功,剩余票數:" + countTicket);
return false;
}
}
}
class Test {
public static void main(String[] args) {
SaleTicket s1 = new SaleTicket();
Thread t1 = new Thread(s1);
Thread t2 = new Thread(s1);
Thread t3 = new Thread(s1);
t1.setName("視窗A");
t2.setName("視窗B");
t3.setName("視窗C");
//啟用三條執行緒
t1.start();
t2.start();
t3.start();
}
}
3.Lock
在JDK5以后新增了一個鎖物件Lock, Lock是介面不能實體化,所有呼叫它的實作類ReentrantLock來實體化
public class SaleTicket implements Runnable {
//統計剩余票數
private int countTicket = 100;
//實體化一個ReentrantLock物件
private ReentrantLock rtl = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
rtl.lock();
if (countTicket <= 0) {
break;
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
countTicket--;
System.out.println(Thread.currentThread().getName() + "售票成功,剩余票數:" + countTicket);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
rtl.unlock();
}
}
}
}
class Test {
public static void main(String[] args) {
SaleTicket s1 = new SaleTicket();
Thread t1 = new Thread(s1);
Thread t2 = new Thread(s1);
Thread t3 = new Thread(s1);
t1.setName("視窗A");
t2.setName("視窗B");
t3.setName("視窗C");
//啟用三條執行緒
t1.start();
t2.start();
t3.start();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/547809.html
標籤:其他
