synchronized 和Lock的使用

synchronized 和 Lock的區別

  1. synchronized是java中的一個關鍵字,也就是說是Java語言內置的特性。
  2. Lock不是Java語言內置的,Lock是一個接口。
  3. Lock和synchronized有一點非常大的不同,採用synchronized不需要用戶去手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完之後,系統會自動讓線程釋放對鎖的佔用;而Lock則必須要用戶去手動釋放鎖,如果沒有主動釋放鎖,就有可能導致出現死鎖現象。
synchronized 鎖方法, 這個方法將不能被同時訪問。
synchronized 鎖對象,這個對象將不能被同時訪問。
當synchronized方法或者synchronized代碼塊執行完之後,系統會自動讓線程釋放對鎖的佔用

synchronized 搶單的實現

  1. 首先對於搶單我們不能鎖住整個方法,我們應該鎖住的是單號。單號是字符串,存在於常量池。所以我們可以鎖住相同的單號。
// 訂單類
class OrderDemo {

    private String name;

    private String orderNum;

    public OrderDemo(String name, String orderNum) {
        this.name = name;
        this.orderNum = orderNum;
    }

    public String getOrderNum() {
        return orderNum;
    }

    public String getName() {
        return name;
    }
}

// 業務方法

public static void test(OrderDemo orderDemo) {
    synchronized (orderDemo.getOrderNum()){
        System.out.println(orderDemo.getName() + "獲得了鎖");
        System.out.println(orderDemo.getName() + "釋放了鎖");
    }
}

// 測試方法

public static void main(String[] a){
    OrderDemo orderDemo = new OrderDemo("線程1","001002003");
    Thread thread = new Thread(() -> test(orderDemo));
    OrderDemo orderDemo2 = new OrderDemo("線程2","001002003");
    Thread thread2 = new Thread(() -> test(orderDemo2));
    OrderDemo orderDemo3 = new OrderDemo("線程3","001002003");
    Thread thread3 = new Thread(() -> test(orderDemo3));
    OrderDemo orderDemo4 = new OrderDemo("線程4","001002004");
    Thread thread4 = new Thread(() -> test(orderDemo4));
    thread.start();
    thread2.start();
    thread3.start();
    thread4.start();
}

打印結果如下

線程1獲得了鎖
線程4獲得了鎖
線程4釋放了鎖
線程1釋放了鎖
線程3獲得了鎖
線程3釋放了鎖
線程2獲得了鎖
線程2釋放了鎖
  1. 可以看到,對於相同訂單號(001002003)的訂單,會鎖住訂單號,一直等到鎖釋放。下一個訂單纔可以進行處理。
  2. 對於訂單號不同的訂單會並行執行。

Lock

方法 說明 參數說明
lock() 獲得鎖
tryLock() 返回boolean, ture代表鎖沒有被佔用,獲得鎖。false代表鎖被佔用
tryLock(long time, TimeUnit unit) 返回boolean, 等待time時長後,鎖還沒有被釋放。返回false。time時長內釋放了返回true, 並獲得鎖 time:時長, unit:時間類型
lockInterruptibly() 獲得鎖,和lock()不同,舉例:當A線程處於等待時(對執行中的線程無效),其餘的線程可調用A線程的interrupt()來中斷A線程,A線程直接返回,並拋出InterruptException異常。如果是lock()的話,A線程還是會繼續等待並執行,只是給A線程做了箇中斷標誌
unlock() 釋放鎖

lock()

// 業務測試
    public static void test2(OrderDemo orderDemo) {
        Lock lock = map.get(orderDemo.getOrderNum());
        lock.lock();
        try {
            System.out.println(orderDemo.getName()+"得到了鎖");
            Thread.sleep(1000);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println(orderDemo.getName()+"釋放了鎖");
            lock.unlock();
        }
    }
    
// 測試
    public static void main(String[] a){
        Lock lock1 = new ReentrantLock();
        map.put("001002003", lock1);
        Lock lock2 = new ReentrantLock();
        map.put("001002004", lock2);
        OrderDemo orderDemo = new OrderDemo("線程1","001002003");
        Thread thread = new Thread(() -> test2(orderDemo));
        OrderDemo orderDemo2 = new OrderDemo("線程2","001002003");
        Thread thread2 = new Thread(() -> test2(orderDemo2));
        OrderDemo orderDemo3 = new OrderDemo("線程3","001002003");
        Thread thread3 = new Thread(() -> test2(orderDemo3));
        OrderDemo orderDemo4 = new OrderDemo("線程4","001002004");
        Thread thread4 = new Thread(() -> test2(orderDemo4));
        OrderDemo orderDemo5= new OrderDemo("線程5","001002004");
        Thread thread5 = new Thread(() -> test2(orderDemo5));
        thread.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();

    }

打印如下

線程1得到了鎖
線程5得到了鎖
線程5釋放了鎖
線程4得到了鎖
線程1釋放了鎖
線程3得到了鎖
線程3釋放了鎖
線程4釋放了鎖
線程2得到了鎖
線程2釋放了鎖
1. 將訂單號和Lock 進行綁定。確保相同的單號共享一個鎖。
2. 可以看到,對於相同訂單號的訂單,會鎖住訂單號,一直等到鎖釋放。下一個訂單纔可以進行處理。
3. 對於訂單號不同的訂單會並行執行。

tryLock()

    // 業務方法
    public static void test3(OrderDemo orderDemo) {
        Lock lock = map.get(orderDemo.getOrderNum());
        if (lock.tryLock()) {
            try {
                System.out.println(orderDemo.getName() + "得到了鎖");
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println(orderDemo.getName() + "釋放了鎖");
                lock.unlock();
            }
        } else {
            System.out.println(orderDemo.getName() + "獲取鎖失敗, 鎖已被佔用");
        }
    }
    
// 測試方法
    public static void main(String[] a){
        Lock lock1 = new ReentrantLock();
        map.put("001002003", lock1);
        Lock lock2 = new ReentrantLock();
        map.put("001002004", lock2);
        OrderDemo orderDemo = new OrderDemo("線程1","001002003");
        Thread thread = new Thread(() -> test3(orderDemo));
        OrderDemo orderDemo2 = new OrderDemo("線程2","001002003");
        Thread thread2 = new Thread(() -> test3(orderDemo2));
        OrderDemo orderDemo3 = new OrderDemo("線程3","001002003");
        Thread thread3 = new Thread(() -> test3(orderDemo3));
        OrderDemo orderDemo4 = new OrderDemo("線程4","001002004");
        Thread thread4 = new Thread(() -> test3(orderDemo4));
        OrderDemo orderDemo5= new OrderDemo("線程5","001002004");
        Thread thread5 = new Thread(() -> test3(orderDemo5));
        thread.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();

    }

打印如下

線程1得到了鎖
線程4得到了鎖
線程2獲取鎖失敗, 鎖已被佔用
線程5獲取鎖失敗, 鎖已被佔用
線程3獲取鎖失敗, 鎖已被佔用
線程4釋放了鎖
線程1釋放了鎖
我們可以看到線程1得到了鎖,而和它共享鎖的線程2和線程3都沒有得到鎖。
線程4得到了鎖,而和它共享鎖的線程5沒有得到鎖。

tryLock(long time, TimeUnit unit)

// 業務方法
    public static void test4(OrderDemo orderDemo){
        Lock lock = map.get(orderDemo.getOrderNum());
        try {
            if (lock.tryLock(5, TimeUnit.SECONDS)) {
                try {
                    System.out.println(orderDemo.getName() + "得到了鎖");
                    Thread.sleep(3000);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(orderDemo.getName() + "釋放了鎖");
                    lock.unlock();
                }
            } else {
                System.out.println(orderDemo.getName() + "獲取鎖失敗, 鎖已被佔用");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
// 測試方法
    public static void main(String[] a){
        Lock lock1 = new ReentrantLock();
        map.put("001002003", lock1);
        Lock lock2 = new ReentrantLock();
        map.put("001002004", lock2);
        OrderDemo orderDemo = new OrderDemo("線程1","001002003");
        Thread thread = new Thread(() -> test4(orderDemo));
        OrderDemo orderDemo2 = new OrderDemo("線程2","001002003");
        Thread thread2 = new Thread(() -> test4(orderDemo2));
        OrderDemo orderDemo3 = new OrderDemo("線程3","001002003");
        Thread thread3 = new Thread(() -> test4(orderDemo3));
        OrderDemo orderDemo4 = new OrderDemo("線程4","001002004");
        Thread thread4 = new Thread(() -> test4(orderDemo4));
        OrderDemo orderDemo5= new OrderDemo("線程5","001002004");
        Thread thread5 = new Thread(() -> test4(orderDemo5));
        thread.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();
    }    

打印

線程1得到了鎖
線程4得到了鎖
線程4釋放了鎖
線程1釋放了鎖
線程5得到了鎖
線程2得到了鎖
線程3獲取鎖失敗, 鎖已被佔用
線程5釋放了鎖
線程2釋放了鎖
1. 程序中寫了每個線程最少會鎖3秒的時間。但線程只會等待五秒。現在線程(1,2,3)共享一個鎖,線程(4,5)共享一個鎖。可以猜到,線程(1,2,3)會只有兩個會獲取到鎖,而線程(4,5)會都獲得鎖。而打印也證實了我們的猜測。

lockInterruptibly()

// 業務
    public static void test5(OrderDemo orderDemo, Thread thread) throws InterruptedException {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
        Lock lock = map.get(orderDemo.getOrderNum());
        lock.lockInterruptibly();
        System.out.println(dateFormat.format(new Date()) + ":" +  orderDemo.getName() + "得到了鎖");
        try {
            // 判斷線程是否被中斷
            if (!thread.isInterrupted()) {
                Thread.sleep(2000);
            } else {
                System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "被中斷");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "釋放了鎖");
            lock.unlock();
        }
    }
// 測試方法
    public static void main(String[] a) {
        Lock lock1 = new ReentrantLock();
        map.put("001002003", lock1);
        OrderDemo orderDemo = new OrderDemo("線程1", "001002003");
        Thread thread = new Thread(() -> {
            try {
                test5(orderDemo, Thread.currentThread());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        OrderDemo orderDemo2 = new OrderDemo("線程2", "001002003");
        Thread thread2 = new Thread(() -> {
            try {
                test5(orderDemo2, Thread.currentThread());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        OrderDemo orderDemo3 = new OrderDemo("線程3", "001002003");
        Thread thread3 = new Thread(() -> {
            try {
                test5(orderDemo3, Thread.currentThread());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        thread2.start();
        thread3.start();

        try {
            Thread.sleep(1000);
            thread3.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

打印

2019082818:06:29:線程1得到了鎖
java.lang.InterruptedException
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
	at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
	at com.lzq.collection.SynchronizedDemo.test5(SynchronizedDemo.java:130)
	at com.lzq.collection.SynchronizedDemo.lambda$main$2(SynchronizedDemo.java:47)
	at java.lang.Thread.run(Thread.java:745)
2019082818:06:31:線程1釋放了鎖
2019082818:06:31:線程2得到了鎖
2019082818:06:33:線程2釋放了鎖
此代碼實現在1秒後中斷線程3的線程。使用lockInterruptibly獲取鎖的話,線程3會直接中斷返回並拋出InterruptedException異常。線程不會繼續等待。

如果 lock.lockInterruptibly() 換成 lock.lock() 線程3不會被立即中斷。還是會繼續等待並執行。只是對線程3做了箇中斷狀態。

換成lock.lock()打印如下

2019年08月28日 18:08:32:線程1得到了鎖
2019年08月28日 18:08:34:線程1釋放了鎖
2019年08月28日 18:08:34:線程2得到了鎖
2019年08月28日 18:08:36:線程2釋放了鎖
2019年08月28日 18:08:36:線程3得到了鎖
2019年08月28日 18:08:36:線程3被中斷
2019年08月28日 18:08:36:線程3釋放了鎖

可以看出線程3還是繼續等待並執行,只是做了箇中斷狀態

隨堂代碼

package com.lzq.collection;


import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 開發公司:個人
 * 版權:個人
 * <p>
 * ListDemo
 *
 * @author 劉志強
 * @created Create Time: 2019/8/26
 */
public class SynchronizedDemo {

    static Map<String,Lock> map = new HashMap<>();


//    public static void main(String[] a){
//        Lock lock1 = new ReentrantLock();
//        map.put("001002003", lock1);
//        Lock lock2 = new ReentrantLock();
//        map.put("001002004", lock2);
//        OrderDemo orderDemo = new OrderDemo("線程1","001002003");
//        Thread thread = new Thread(() -> test3(orderDemo));
//        OrderDemo orderDemo2 = new OrderDemo("線程2","001002003");
//        Thread thread2 = new Thread(() -> test3(orderDemo2));
//        OrderDemo orderDemo3 = new OrderDemo("線程3","001002003");
//        Thread thread3 = new Thread(() -> test3(orderDemo3));
//        OrderDemo orderDemo4 = new OrderDemo("線程4","001002004");
//        Thread thread4 = new Thread(() -> test3(orderDemo4));
//        OrderDemo orderDemo5= new OrderDemo("線程5","001002004");
//        Thread thread5 = new Thread(() -> test3(orderDemo5));
//        thread.start();
//        thread2.start();
//        thread3.start();
//        thread4.start();
//        thread5.start();
//    }

    public static void main(String[] a) {
        Lock lock1 = new ReentrantLock();
        map.put("001002003", lock1);
        OrderDemo orderDemo = new OrderDemo("線程1", "001002003");
        Thread thread = new Thread(() -> {
            try {
                test5(orderDemo, Thread.currentThread());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        OrderDemo orderDemo2 = new OrderDemo("線程2", "001002003");
        Thread thread2 = new Thread(() -> {
            try {
                test5(orderDemo2, Thread.currentThread());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        OrderDemo orderDemo3 = new OrderDemo("線程3", "001002003");
        Thread thread3 = new Thread(() -> {
            try {
                test5(orderDemo3, Thread.currentThread());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        thread2.start();
        thread3.start();

        try {
            Thread.sleep(1000);
            thread3.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static void test(OrderDemo orderDemo) {
        synchronized (orderDemo.getOrderNum()){
            System.out.println(orderDemo.getName() + "獲得了鎖");
            System.out.println(orderDemo.getName() + "釋放了鎖");
        }
    }



    public static void test2(OrderDemo orderDemo) {
        Lock lock = map.get(orderDemo.getOrderNum());
        lock.lock();
        try {
            System.out.println(orderDemo.getName()+"得到了鎖");
            Thread.sleep(1000);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println(orderDemo.getName()+"釋放了鎖");
            lock.unlock();
        }
    }

    public static void test3(OrderDemo orderDemo) {
        Lock lock = map.get(orderDemo.getOrderNum());
        if (lock.tryLock()) {
            try {
                System.out.println(orderDemo.getName() + "得到了鎖");
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println(orderDemo.getName() + "釋放了鎖");
                lock.unlock();
            }
        } else {
            System.out.println(orderDemo.getName() + "獲取鎖失敗, 鎖已被佔用");
        }
    }

    public static void test4(OrderDemo orderDemo){
        Lock lock = map.get(orderDemo.getOrderNum());
        try {
            if (lock.tryLock(5, TimeUnit.SECONDS)) {
                try {
                    System.out.println(orderDemo.getName() + "得到了鎖");
                    Thread.sleep(3000);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(orderDemo.getName() + "釋放了鎖");
                    lock.unlock();
                }
            } else {
                System.out.println(orderDemo.getName() + "獲取鎖失敗, 鎖已被佔用");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    public static void test5(OrderDemo orderDemo, Thread thread) throws InterruptedException {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
        Lock lock = map.get(orderDemo.getOrderNum());
        lock.lock();
        System.out.println(dateFormat.format(new Date()) + ":" +  orderDemo.getName() + "得到了鎖");
        try {
            // 判斷線程是否被中斷
            if (!thread.isInterrupted()) {
                Thread.sleep(2000);
            } else {
                System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "被中斷");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "釋放了鎖");
            lock.unlock();
        }
    }
}

class OrderDemo {

    private String name;

    private String orderNum;

    public OrderDemo(String name, String orderNum) {
        this.name = name;
        this.orderNum = orderNum;
    }

    public String getOrderNum() {
        return orderNum;
    }

    public String getName() {
        return name;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章