Java----線程的同步
引入問題
在關於線程的操作時,往往會遇到線程的安全問題。
這裏我用一個例子來說明:
package com.CharlesLC_Test;
public class Window_Test {
public static void main(String[] args) {
Window window = new Window();
Thread window1 = new Thread(window);
Thread window2 = new Thread(window);
Thread window3 = new Thread(window);
window1.setName("窗口一");
window2.setName("窗口二");
window3.setName("窗口三");
window1.start();
window2.start();
window3.start();
}
}
class Window implements Runnable{
private int tickets = 100;
@Override
public void run() {
while(true) {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":成功取票"+tickets+"還剩下" + --tickets);
}
else{break;}
}
}
}
結果:發現窗口出現了重票
問題所在:
對多條語句只執行了一部分,還沒有執行完,另一個線程參與進來執行。導致共享數據的錯誤。
線程同步的方式
使用Synchronized.
當一個線程調用對象的一段synchronized代碼時,需要先獲取這個鎖,然後去執行相應的代碼,執行結束之後,釋放鎖。當一個線程拿到這個鎖時,其他線程不能進入被這個鎖起來的這段代碼。
同步代碼塊
使用方法:
synchronized(對象){//需要被同步的代碼;}
這裏我修改了取票這個例子中的Window類:
class Window implements Runnable{
private int tickets = 100;
@Override
public void run() {
while(true) {
// 這裏作了改變,把這段代碼用 synchronized鎖了起來
synchronized (this) {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":成功取票" + tickets + "還剩下" + --tickets);
} else {
break;
}
}
}
}
}
結果:
同步方法:
使用方法:
public synchronized void show(String name){ }
修改過的Window類
class Window implements Runnable{
private int tickets = 100;
@Override
public void run() {
while(true) {
show();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets == 0){
break;
}
}
}
private synchronized void show() {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + ":成功取票" + tickets + "還剩下" + --tickets);
}
}
}
結果:
- 當前線程的同步方法、同步代碼塊執行結束。
- 當前線程在同步代碼塊、同步方法中遇到break、return終止了該代碼塊、該方法的繼續執行。
- 當前線程在同步代碼塊、同步方法中出現了未處理的Error或Exception,導致異常結束。
- 當前線程在同步代碼塊、同步方法中執行了線程對象的wait()方法,當前線程暫停,並釋放鎖。
注意:必須確保使用同一個資源的多個線程共用一把鎖,這個非常重要,否則就無法保證共享資源的安全
使用Lock()
導入
import java.util.concurrent.locks.ReentrantLock;
使用方式:
class A{
private final ReentrantLock lock=new ReenTrantLock();
public void m(){
lock.lock()
try{
//保證線程安全的代碼;
finally{
lock.unlock();
}
synchronized與Lock的對比
1.Lock是顯式鎖(手動開啓和關閉鎖,別忘記關閉鎖),synchronized是隱式鎖,出了作用域自動釋放
2.Lock只有代碼塊鎖,synchronized有代碼塊鎖和方法鎖
3.使用Lock鎖,JVM將花費較少的時間來調度線程,性能更好。並且具有更好的擴展性(提供更多的子類)