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;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章