執行緒的同步
阿新 • • 發佈:2020-07-21
執行緒的同步
執行緒的安全問題
- 多個執行緒執行的不確定性引起執行的結果的不穩定性
- 多個執行緒對資料的共享,會造成操作的不完整性、會破壞資料(例如視窗買票問題,多個視窗對票數進行共享,會出現兩個視窗賣號碼相同的票給不同的人)
通過同步機制解決執行緒安全問題
方法一:同步程式碼塊
格式
synchronized(同步監視器){
需要被同步的程式碼
}
舉例說明
class Thread implements Runnable{ private Object obj = new Object(); public void run() { //使用類物件充當鎖 synchronized(obj){ ....... } } }
說明
- 操作共享資料的程式碼即為需要被同步的程式碼
- 不能多包含程式碼,也不能少包含程式碼
- 共享資料:多個執行緒共同操作的變數
- 同步監視器:俗稱鎖
- 任何一個類的物件都可以來充當鎖
- 要求多個執行緒必須共用同一把鎖
- 在實現Runnable介面建立多執行緒的方式中,考慮使用this充當同步監視器
- 在繼承Thread類建立多執行緒的方式中,慎用this來充當同步監視器,考慮使用當前類來充當同步監視器
特點
- 好處:解決執行緒的安全問題
- 侷限性:操作同步程式碼時,只能有一個執行緒參與,其他執行緒等待。相當於一個單執行緒的過程,效率低
程式碼實現
實現Runnable介面建立多執行緒的方式
/** * 建立三個視窗買票,票數100張:使用實現Runnable介面的方式實現的 */ class WindowThread implements Runnable{ private int ticket = 100; // private Object obj = new Object(); public void run() { while (true) { //此時this:唯一的WindowThread物件 synchronized(this){// 方式二:synchronized(obj){ if (ticket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread(). getName() + ":" + "買票,票號為" + ticket); ticket--; }else{ break; } } } } } public class Test1 { public static void main(String[] args) { WindowThread window = new WindowThread(); Thread w1 = new Thread(window); Thread w2 = new Thread(window); Thread w3 = new Thread(window); w1.setName("視窗1"); w1.start(); w2.setName("視窗2"); w2.start(); w3.setName("視窗3"); w3.start(); } }
繼承Thread類建立多執行緒的方式
class Window extends Thread {
// 大家公用資料,只有100張票
private static int ticket = 100;
private static Object obj = new Object();
public void run() {
while (true) {
//方式二
synchronized(Window.class){
// 方式一:synchronized(obj){
//synchronized(this)錯誤的,此時this代表著三個物件
if(ticket > 0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + ":" + "買票,票號為" + ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class Test2 {
public static void main(String[] args) {
Window w1 = new Window();
Window w2 = new Window();
Window w3 = new Window();
w1.setName("視窗1");
w2.setName("視窗2");
w3.setName("視窗3");
w1.start();
w2.start();
w3.start();
}
}
同步方法
如果操作共享資料的程式碼完整的宣告在一個方法中,就可以將此方法宣告同步的
格式
利用synchronized 修飾方法
public synchronized void XXX(){
}
或
public static synchronized void XXX(){
}
說明
- synchronized修飾方法時鎖定的是呼叫該方法的物件
- 同步方法仍然涉及到同步監視器,只是不需要我們顯示的宣告
- 非靜態的同步方法,同步監視器是this
- 靜態的同步方法,同步監視器是當前類本身(Window.class)
程式碼實現
實現Runnable介面建立多執行緒的方式
非靜態同步方法,呼叫this
class WindowThread3 implements Runnable{
private int ticket = 100;
private static boolean isFlag = true;
// private Object obj = new Object();
public void run() {
while (isFlag) {
show();
}
}
public synchronized void show(){//同步監視器:this
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread(). getName() + ":" + "買票,票號為" + ticket);
ticket--;
}else{
isFlag = false;
}
}
}
public class Test3 {
public static void main(String[] args) {
WindowThread3 window = new WindowThread3();
Thread w1 = new Thread(window);
Thread w2 = new Thread(window);
Thread w3 = new Thread(window);
w1.setName("視窗1");
w1.start();
w2.setName("視窗2");
w2.start();
w3.setName("視窗3");
w3.start();
}
}
繼承Thread類建立多執行緒的方式
靜態同步方法,呼叫當前類本身
class Window4 extends Thread{
private static int ticket = 100;
private static boolean isFlag = true;
@Override
public void run() {
while(isFlag){
show();
}
}
public static synchronized void show(){
//同步監視器:Window.class
if(ticket > 0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread(). getName() + ":" + "買票,票號為" + ticket);
ticket--;
}else{
isFlag = false;
}
}
}
public class Test4 {
public static void main(String[] args) {
Window4 w1 = new Window4();
Window4 w2 = new Window4();
Window4 w3 = new Window4();
w1.setName("視窗1");
w2.setName("視窗2");
w3.setName("視窗3");
w1.start();
w2.start();
w3.start();
}
}