Java Lock锁

Java Lock锁

概述

synchronized被称为隐式锁,会自动获取释放锁,是非公平锁;Lock被称为显示锁,需要显示的手动获取释放锁,可以设置是否为公平锁。

Lock与synchronized一样可以实现线程同步,但比synchronized更强大,粒度更小,更灵活。

  • lock()获取锁。
  • unlock()释放锁。

ReentrantLock

显示锁的基本使用。

Lock#lock()

public class Demo {
    public static void main(String[] args) {
        Task task = new Task();
        new Thread(task).start();
        new Thread(task).start();
    }
}

class Task implements Runnable {
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        lock.lock();
        try {
            Thread.sleep(3000L);
            System.out.println(Thread.currentThread().getName() + " hello world");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Lock#tryLock()

非阻塞式获取锁。

  • lock()是阻塞锁。
  • tryLock()是非阻塞锁,当锁被占用时,可以执行其他任务;tryLock()支持超时时间,在指定时间内可以获取锁返回true,否则返回false。
public class Demo {
    public static void main(String[] args) {
        Task task = new Task();
        new Thread(task).start();
        new Thread(task).start();
    }
}

class Task implements Runnable {
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        if (lock.tryLock()) {
            try {
                Thread.sleep(3000L);
                System.out.println(Thread.currentThread().getName() + ": hello world");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println(Thread.currentThread().getName() + ": 锁被占用,执行其他任务!");
        }
    }
}

Lock#lockInterruptibly()

不可中断等待锁的线程:

public class Demo {
    public static void main(String[] args) {
        Task task = new Task();
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //中断线程
        t2.interrupt();
    }
} 

class Task implements Runnable {
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        lock.lock();
        try {
            Thread.sleep(3000L);
            System.out.println(Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

输出:

java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at com.example.lib_java.Task.run(Demo.java:35)
	at java.base/java.lang.Thread.run(Thread.java:834)
Thread-0

可中断等待锁的线程:

public class Demo {
    public static void main(String[] args) {
        Task task = new Task();
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //中断线程
        t2.interrupt();
    }
}

class Task implements Runnable {
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + "被中断");
            return;
        }
        try {
            Thread.sleep(3000L);
            System.out.println(Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

输出:

Thread-1被中断
Thread-0

await() & signal() & signalAll()

Condition的用法基本与synchronized的等待唤醒基本一致。

synchronizedCondition
wait()await()
wait(long timeout)await(long time, TimeUnit unit)
notify()signal()
notifyAll()signalAll()

await() 使用:

public class Demo {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        Task task = new Task(lock, condition);
        new Thread(task).start();
    }
}

class Task implements Runnable {
    private Lock lock;
    private Condition condition;

    public Task(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }

    @Override
    public void run() {
        lock.lock();
        try {
            //等待
            condition.await();
            System.out.println("hello world");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

说明:线程处于等待阶段,“hello world”一直没有输出。

await() & signal() 使用:

public class Demo {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        Task task = new Task(lock, condition);
        new Thread(task).start();
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        lock.lock();
        //唤醒
        condition.signal();
        lock.unlock();
    }
}

class Task implements Runnable {
    private Lock lock;
    private Condition condition;

    public Task(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }

    @Override
    public void run() {
        lock.lock();
        try {
            //等待
            condition.await();
            System.out.println("hello world");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

说明:线程进入等待阶段,1秒后被唤醒,“hello world”被输出。

await() & signalAll() 使用:

public class Demo {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        Task task = new Task(lock, condition);
        new Thread(task).start();
        new Thread(task).start();
        new Thread(task).start();
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        lock.lock();
        //唤醒所有等待
        condition.signalAll();
        lock.unlock();
    }
}

class Task implements Runnable {
    private Lock lock;
    private Condition condition;

    public Task(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }

    @Override
    public void run() {
        lock.lock();
        try {
            condition.await();
            System.out.println("hello world");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

说明:1秒后,三个线程都被唤醒,输出了3个“hello world”。

读写锁(ReadWriteLock)

  • 读锁:也称为共享锁,线程共享的,用于读取操作的同步锁,锁对象是ReentrantReadWriteLock.ReadLock
  • 写锁:也称为独占锁,线程独占的,用于读取操作的同步锁,锁对象是ReentrantReadWriteLock.WriterLock

论证读锁是共享锁

public class Demo {
    public static void main(String[] args) {
        Task task = new Task();
        Thread t1 = new Thread(task, "线程一");
        Thread t2 = new Thread(task, "线程二");
        Thread t3 = new Thread(task, "线程三");
        t1.start();
        t2.start();
        t3.start();
    }
}

class Task implements Runnable {
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
    private Lock readLock = readWriteLock.readLock();

    @Override
    public void run() {
        readLock.lock();
        try {
            Thread.sleep(3000L);
            System.out.println(Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            readLock.unlock();
        }
    }
}

说明:同时输出三条内容,说明三个线程同时拿到锁,所以读锁是共享的。

论证写锁是独占的

public class Demo {
    public static void main(String[] args) {
        Task task = new Task();
        Thread t1 = new Thread(task, "线程一");
        Thread t2 = new Thread(task, "线程二");
        Thread t3 = new Thread(task, "线程三");
        t1.start();
        t2.start();
        t3.start();
    }
}

class Task implements Runnable {
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
    private Lock writeLock = readWriteLock.writeLock();

    @Override
    public void run() {
        writeLock.lock();
        try {
            Thread.sleep(3000L);
            System.out.println(Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            writeLock.unlock();
        }
    }
}

说明:依次输出三个线程的信息,说明三个线程是依次获取锁,所以写锁是独占的。

使用读写锁实现高并发容器

public class Demo {
    public static void main(String[] args) {
        //数据存储容器
        RWDictionary dictionary = new RWDictionary();
        //写任务
        WriteTask writeTask = new WriteTask(dictionary);
        //读任务
        ReadTask readTask = new ReadTask(dictionary);

        Thread write1 = new Thread(writeTask, "写入线程一");
        Thread write2 = new Thread(writeTask, "写入线程二");
        Thread write3 = new Thread(writeTask, "写入线程三");
        Thread read1 = new Thread(readTask, "读取线程一");
        Thread read2 = new Thread(readTask, "读取线程二");
        Thread read3 = new Thread(readTask, "读取线程三");
        write1.start();
        write2.start();
        write3.start();
        read1.start();
        read2.start();
        read3.start();
    }
}

/**
 * 写任务
 */
class WriteTask implements Runnable {
    private RWDictionary dictionary;

    public WriteTask(RWDictionary dictionary) {
        this.dictionary = dictionary;
    }

    @Override
    public void run() {
        int i = 0;
        while (true) {
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String name = Thread.currentThread().getName();
            dictionary.put("hello", name + "---" + i++);
        }
    }
}

/**
 * 读任务
 */
class ReadTask implements Runnable {
    private RWDictionary dictionary;

    public ReadTask(RWDictionary dictionary) {
        this.dictionary = dictionary;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String[] keys = dictionary.allKeys();
            for (String key : keys) {
                Object value = dictionary.get(key);
                System.out.println(key + ":" + value);
            }
        }
    }
}

/**
 * 数据管理容器
 */
class RWDictionary {
    //存储数据
    private final HashMap<String, Object> map = new HashMap<>();
    //读写锁
    private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
    //读锁
    private final Lock r = readWriteLock.readLock();
    //写锁
    private final Lock w = readWriteLock.writeLock();

    /**
     * 获取数据
     */
    public Object get(String key) {
        r.lock();
        try {
            return map.get(key);
        } finally {
            r.unlock();
        }
    }

    /**
     * 获取所有键
     */
    public String[] allKeys() {
        r.lock();
        try {
            return map.keySet().toArray(new String[0]);
        } finally {
            r.unlock();
        }
    }

    /**
     * 写入数据
     */
    public Object put(String key, Object value) {
        w.lock();
        try {
            return map.put(key, value);
        } finally {
            w.unlock();
        }
    }

    /**
     * 清理所有数据
     */
    public void clear() {
        w.lock();
        try {
            map.clear();
        } finally {
            w.unlock();
        }
    }
}

输出:

hello:写入线程一---193
hello:写入线程一---193
hello:写入线程一---193
hello:写入线程二---194
hello:写入线程二---194
hello:写入线程二---194
hello:写入线程一---195
hello:写入线程一---195
hello:写入线程一---195
hello:写入线程一---196
hello:写入线程一---196
hello:写入线程一---196
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值