多線程的鎖有: synchronized 和 jdk1.5的Lock
先說synchronized的各種用法:
1. 使用任意同一對象做鎖 (一定要是同一對象)
2. 使用this做鎖
3. class字節碼文件做鎖
4. 靜態同步代碼塊做鎖 (原理其實就是:class字節碼文件做鎖)
5.非靜態同步代碼塊做鎖 (原理其實就是:使用this做鎖)
任意對象做鎖例子:
/**
* @author: wangqinmin
* @date : 2020/7/3
* @description: 仰天大笑出門去,我輩豈是蓬蒿人
*/
public class TicketRunnable implements Runnable {
/**
* 需求現在有100張火車票,有兩個窗口同時搶火車票,請使用多線程模擬搶票效果。
*/
int tickets = 100;
private Object obj = new Object();
public void run() {
while (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
sale();
}
}
public void sale() {
/**
* 使用任意、同一對象做鎖
*/
synchronized (obj) {
/**
* 最後判斷一下,還是因爲有線程安全的問題。
*
* 最後一張票賣完後,tickets = 0,但是這時候,最後一個等待的線程剛剛就就進來了,所以會得到100 - 0 + 1 ,結果爲101的票,所以在這裏判斷一下。
* 還有一個辦法,就是在 while (tickets > 0) 之前就加 synchronized。
* 但是使用synchronized最好包裹的代碼很少,所以就這樣寫了。
* 又如果在while那裏就包裹了,多半這個代碼直接就一個線程執行完了。
*/
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - tickets + 1) + "票");
tickets--;
}
}
}
}
this做鎖的例子:
/**
* @author: wangqinmin
* @date : 2020/7/3
* @description: 仰天大笑出門去,我輩豈是蓬蒿人
*/
public class TicketRunnable implements Runnable {
/**
* 需求現在有100張火車票,有兩個窗口同時搶火車票,請使用多線程模擬搶票效果。
*/
int tickets = 100;
public void run() {
while (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
sale();
}
}
public void sale() {
/**
* this做鎖
*/
synchronized (this) {
/**
* 最後判斷一下,還是因爲有線程安全的問題。
*
* 最後一張票賣完後,tickets = 0,但是這時候,最後一個等待的線程剛剛就就進來了,所以會得到100 - 0 + 1 ,結果爲101的票,所以在這裏判斷一下。
* 還有一個辦法,就是在 while (tickets > 0) 之前就加 synchronized。
* 但是使用synchronized最好包裹的代碼很少,所以就這樣寫了。
* 又如果在while那裏就包裹了,多半這個代碼直接就一個線程執行完了。
*/
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - tickets + 1) + "票");
tickets--;
}
}
}
}
class字節碼文件做鎖的例子:
/**
* @author: wangqinmin
* @date : 2020/7/3
* @description: 仰天大笑出門去,我輩豈是蓬蒿人
*/
public class TicketRunnable implements Runnable {
/**
* 需求現在有100張火車票,有兩個窗口同時搶火車票,請使用多線程模擬搶票效果。
*/
int tickets = 100;
private Object obj = new Object();
public void run() {
while (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
sale();
}
}
public void sale() {
/**
* class文件做鎖
*/
synchronized (TicketRunnable.class) {
/**
* 最後判斷一下,還是因爲有線程安全的問題。
*
* 最後一張票賣完後,tickets = 0,但是這時候,最後一個等待的線程剛剛就就進來了,所以會得到100 - 0 + 1 ,結果爲101的票,所以在這裏判斷一下。
* 還有一個辦法,就是在 while (tickets > 0) 之前就加 synchronized。
* 但是使用synchronized最好包裹的代碼很少,所以就這樣寫了。
* 又如果在while那裏就包裹了,多半這個代碼直接就一個線程執行完了。
*/
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - tickets + 1) + "票");
tickets--;
}
}
}
}
靜態同步代碼塊做鎖: (並證明原理爲 字節碼文件爲鎖)
/**
* @author: wangqinmin
* @date : 2020/7/3
* @description: 仰天大笑出門去,我輩豈是蓬蒿人
*/
public class TicketRunnable implements Runnable {
/**
* 需求現在有100張火車票,有兩個窗口同時搶火車票,請使用多線程模擬搶票效果。
* <p>
* 使用靜態變量
*/
private static int tickets = 100;
private Object obj = new Object();
public void run() {
while (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets % 2 != 0) {
synchronized (TicketRunnable.class) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - tickets + 1) + "票");
tickets--;
}
}
} else {
sale();
}
}
}
/**
* 定義靜態同步代碼塊
* 在方法上,加上 static 和 synchronized。
* <p>
* 原理: 這個其實就是使用的字節碼文件做鎖。
* <p>
* 怎麼證明呢 ? 可以寫兩個線程,一個使用字節碼文件做鎖,一個使用靜態同步代碼塊,如果完成了同步功能,就證明靜態同步代碼快使用的是 字節碼文件做鎖。
* <p>
* 在工作中,不推薦使用靜態同步鎖,因爲靜態方法不會被回收。
* 建議使用 非靜態同步代碼塊做鎖;
*/
public static synchronized void sale() {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - tickets + 1) + "票");
tickets--;
}
}
}
非靜態同步代碼塊做鎖:(並證明原理爲: this做鎖)
/**
* @author: wangqinmin
* @date : 2020/7/3
* @description: 仰天大笑出門去,我輩豈是蓬蒿人
*/
public class TicketRunnable implements Runnable {
/**
* 需求現在有100張火車票,有兩個窗口同時搶火車票,請使用多線程模擬搶票效果。
* <p>
* 使用靜態變量
*/
private int tickets = 100;
private Object obj = new Object();
public void run() {
while (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* 使用判斷,證明非靜態同步代碼快使用的是this鎖
*/
if (tickets % 2 != 0) {
synchronized (this) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - tickets + 1) + "票");
tickets--;
}
}
} else {
sale();
}
}
}
/**
* 定義非靜態同步代碼塊
* 在方法上,加上 synchronized。
* <p>
* 原理: 這個其實就是使用的this鎖。
* <p>
* 怎麼證明呢 ? 可以寫兩個線程,一個使用this鎖,一個使用非靜態同步代碼塊,如果完成了同步功能,就證明非靜態同步代碼快使用的是this鎖。
*/
public synchronized void sale() {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - tickets + 1) + "票");
tickets--;
}
}
}
上面都是各種做鎖的例子, 這裏是創建多線程,並調用:
/**
* @author: wangqinmin
* @date : 2020/7/3
* @description: 仰天大笑出門去,我輩豈是蓬蒿人
*/
public class Test {
public static void main(String[] args) {
TicketRunnable ticket = new TicketRunnable();
Thread t1 = new Thread(ticket, "窗口1");
Thread t2 = new Thread(ticket, "窗口2");
t1.start();
t2.start();
}
}