文章思路
- 哪些概念難理解
- 類鎖和對象鎖區別
- 類鎖所有對象一把鎖
- 對象鎖一個對象一把鎖,多個對象多把鎖
- 同步是對同一把鎖而言的,同步這個概念是在多個線程爭奪同一把鎖的時候才能實現的,如果多個線程爭奪不同的鎖,那多個線程是不能同步的
- 兩個線程一個取對象鎖,一個取類鎖,則不能同步
- 兩個線程一個取a對象鎖,一個取b對象鎖,則不能同步
- 類鎖和對象鎖區別
- 文章關鍵內容:
- 什么是鎖
- 鎖有幾種
- 什么是synchronized
- synchronized用法
- 用於方法
- 得到類鎖
- 得到對象鎖
- 用與代碼塊
- 得到類鎖
- 得到對象鎖
- 用於方法
- 兩種鎖的使用
synchronized作用
synchronized是用來完成多線程條件下同步工作的
若沒有同步,一個方法有多條語句,兩個線程A和B都要都用某個方法,而A線程在調用這個方法的時候,B線程可不會等它多條語句都執行完再去調用這個方法,更有可能的是A線程沒有執行完這個方法,B線程奪得CPU執行權,A對這個方法的調用就停止在它最后執行到的語句位置,直到A下一次搶到CPU執行權A才能繼續執行它未執行完的代碼。
有了同步后,當A獲得執行權並開始執行此方法,B就必須乖乖等A將方法全部執行完之后才有權去執行此方法。
鎖和synchronized的關系
鎖是Java中用來實現同步的工具
而之所以能對方法或者代碼塊實現同步的原因就是
只有拿到鎖的線程才能執行synchronized修飾的方法或代碼塊,且其他線程獲得鎖的唯一方法是等目前拿到鎖的線程執行完方法將鎖釋放,如果synchronized修飾的代碼塊或方法沒執行完是不會釋放這把鎖的,這就保證了拿到鎖的線程一定可以一次性把它調用的方法或代碼塊執行完
synchronized有幾種用法
用於方法
class B {
//在方法前面加上synchronized關鍵字表示作用於該方法
//需要注意方法有兩種,一種靜態方法,一種非靜態方法
//兩者區別在於,當修飾靜態時候,大家都調用的是同一個。當修飾非靜態方法時候,調用的是每個對象自己的那個方法,因為非靜態域或方法是每個對象各自都有一份的,靜態方法是所有對象公有的。
synchronized public static void mB(String value) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
System.out.print(value);
}
}
synchronized public void mC(String value) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
System.out.print(value);
}
}
}
用於代碼塊
class A { public static void test() { //修飾代碼塊的情況也有兩種,這里表示對類進行同步 synchronized (A.class) { System.out.println("haha"); } } public void test2() { //這里表示對當前對象進行同步,兩者區別看下面鎖有幾種 synchronized (this) { System.out.println("haha"); } } }
鎖有幾種
類鎖
類鎖,是用來鎖類的,我們知道一個類的所有對象共享一個class對象,共享一組靜態方法,類鎖的作用就是使持有者可以同步地調用靜態方法。當synchronized指定修飾靜態方法或者class對象的時候,拿到的就是類鎖,類鎖是所有對象共同爭搶一把。
//B中有兩個方法mB和mC
//mB是synchronized修飾靜態方法,拿到類鎖
//mC是synchronized修飾非靜態方法,拿到的也是類鎖
class B {
synchronized public static void mB(String value) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
System.out.print(value);
}
}
public void mC(String value) {
synchronized (B.class) {
for (int i = 0; i < 1000; i++) {
System.out.print(value);
}
}
}
}
對象鎖
對象鎖,是用來對象的,虛擬機為每個的非靜態方法和非靜態域都分配了自己的空間,不像靜態方法和靜態域,是所有對象共用一組。
所以synchronized修飾非靜態方法或者this的時候拿到的就是對象鎖,對象鎖是每個對象各有一把的,即同一個類如果有兩個對象。
//類C中有兩個方法mB和mC
//mB是synchronized非靜態方法,拿到對象鎖
//mC是synchronized修飾this,拿到的也是對象鎖
class C {
synchronized publi void mB(String value) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
System.out.print(value);
}
}
public void mC(String value) {
synchronized (this) {
for (int i = 0; i < 1000; i++) {
System.out.print(value);
}
}
}
}
對象鎖和類鎖的使用
對象鎖
下例中,兩個線程調用同一個對象b的mB方法。最終結果是輸出了1000次“1”之后輸出了1000次“2”。可見兩個線程對此方法的調用實現了同步。
class B { //修飾非靜態方法拿到對象鎖 synchronized public void mB(String name) throws InterruptedException { for (int i = 0; i < 1000; i++) { System.out.print(name); } } //修飾this拿到對象鎖 public void mB2(String name) throws InterruptedException { synchronized(this) { for (int i = 0; i < 1000; i++) { System.out.print(name); } } } } public class test { public static void main(String[] args) { B b = new B(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { try { //線程1的調用處 b.mB("1"); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { //線程2的調用處 b.mB2("2"); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread1.start(); thread2.start(); } }
類鎖
下面代碼中對靜態方法mA和mC的調用實現了同步,結果沒有交替輸出1和2,而是一次性輸出完成后再輸出的另一種
class B { //修飾靜態方法,調用取得類鎖 synchronized public static void mB(String value) throws InterruptedException { for (int i = 0; i < 1000; i++) { System.out.print(value); } } //修飾class對象,調用取得靜類鎖 public static void mC(String value) { synchronized (B.class) { for (int i = 0; i < 1000; i++) { System.out.print(value); } } } public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { try { B.mB("1"); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { B.mC("2"); } }); } }
類鎖和對象鎖同時存在
同時存在的情況下,兩者做不到同步。類鎖和對象鎖是兩種鎖。下述情況,類B的靜態方法和代碼塊功能都是打印100個value值,但是靜態方法是類鎖,而代碼塊鎖this,是對象鎖。所以代碼塊和靜態方法交替執行、交替打印,大家可復制代碼自行驗證。
class B {
//靜態方法,上類鎖,函數功能為連續打印1000個value值,調用時會傳1,所以會打印1000個1
synchronized public static void mB(String value) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
System.out.print(value);
}
}
public void mC(String value) {
//修飾this上對象鎖,函數功能也是連續打印1000個value值,調用時會傳2,所以會打印1000個2
synchronized (this) {
for (int i = 0; i < 1000; i++) {
System.out.print(value);
}
}
}
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
B.mB("1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
new B().mC("2");
}
});
thread.start();
thread2.start();
}
}