认真阅读完这篇文章熟练掌握多线程常见锁的基本用法

简介: 认真阅读完这篇文章熟练掌握多线程常见锁的基本用法

CountDownLatch

官方解释:一种同步辅助,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成countDownLatch用给定的计数进行初始化。由于对countDown方法的调用,await方法会阻塞直到当前计数达到0,之后所有等待的线程都会被释放,所有后续的await调用都会立即返回。这是一个一次性现象——计数不能重置。如果需要重置计数的版本,可以考虑使用CyclicBarrier。countDownLatch是一个通用的同步工具,可以用于多种目的。用一个计数初始化的CountDownLatch作为一个简单的开/关闩锁,或gate:所有调用await的线程在gate处等待,直到它被调用countDown的线程打开。可以使用初始化为N的CountDownLatch使一个线程等待,直到Nthreads完成某些操作,或者某些操作已经完成N次。CountDownLatch的一个有用属性是,它不要求调用countDown的线程在继续之前等待计数达到0,它只是阻止任何线程继续等待,直到所有线程都可以通过。
案例一、

package duoxiancheng2;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * @author yeqv
 * @program A2
 * @Classname T2
 * @Date 2022/2/8 8:38
 * @Email w16638771062@163.com
 */
public class T2 {
    //CountDownLatch
    CountDownLatch latch = new CountDownLatch(1);

    public static void main(String[] args) {

        T2 t = new T2();
        new Thread(() -> t.a(), "A").start();
        new Thread(() -> t.b(), "B").start();
    }

    void a() {
        System.out.println(Thread.currentThread().getName() + "已启动");
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        latch.countDown();
        System.out.println(Thread.currentThread().getName() + "已结束");


    }

    void b() {
        System.out.println(Thread.currentThread().getName() + "已启动");
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "已结束");

    }
}

结果:线程A,B启动。线程A在执行,线程B在等待
在这里插入图片描述
当线程A完成操作开锁之后,线程B开始执行(这里是某组操作完成时,也就是执行了countDown()方法。不是说线程A执行完。可能执行到中间就已经开锁了)
在这里插入图片描述

ReentrantLock

ReentrantLock 最大的特点公平锁
即哪个线程等待的时间最长优先执行
ReentrantLock常常对比着synchronized来分析,我们先对比着来看然后再一点一点分析。
(1)synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。

(2)synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。

(3)synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。

public class T3 {
    Lock lock = new ReentrantLock(true);//设置为true公平锁,效率低但公平

    void m() {
        for (int i = 0; i <= 20; i++) {
            lock.lock();
            System.out.println(Thread.currentThread().getName());
            try{
                TimeUnit.SECONDS.sleep(1);
            }catch(Exception e){
                e.printStackTrace();
            }
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        var t = new T3();
        new Thread(t::m,"T1").start();
        new Thread(t::m,"T2").start();
        new Thread(t::m,"T3").start();
        new Thread(t::m,"T4").start();
    }
}

ReadWriteLock

ReadWriteLock也是一个接口,提供了readLock和writeLock两种锁的操作机制,一个资源可以被多个线程同时读,或者被一个线程写,但是不能同时存在读和写线程。
假设在程序中定义一个共享的数据结构用作缓存,它大部分时间提供读服务(例如:查询和搜索),而写操作占有的时间很少,但是写操作完成之后的更新需要对后续的读服务可见。
特性:
读-读不互斥
读-写互斥
写-写互斥
案例、

public class TestReadWriteLock{
    public static void main(String[] args){
        ReadWriteLockDemo rw = new ReadWriteLockDemo();

        // 一个线程进行写
        new Thread(new Runnable(){
            public void run(){
                rw.set((int)(Math.random()*100));
            }
        },"Write:").start();


        // 100个线程进行读操作
        for(int i=0; i<100; i++){
            new Thread(new Runnable(){
                public void run(){
                rw.get();
                }
            },"Read:").start();
        }
    }
}
 
class ReadWriteLockDemo{
    private int number = 0;
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    // 读
    public void get(){
        lock.readLock().lock(); // 上锁
        try{
            System.out.println(Thread.currentThread().getName()+":"+number);
        }finally{
            lock.readLock().unlock(); // 释放锁
        }
    }
    // 写
    public void set(int number){
        lock.writeLock().lock();
        try{
            System.out.println(Thread.currentThread().getName());
            this.number = number;
        }finally{
            lock.writeLock().unlock();
        }
    }
}
相关文章
|
3月前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
50 2
|
3天前
|
Java 关系型数据库 MySQL
【JavaEE“多线程进阶”】——各种“锁”大总结
乐/悲观锁,轻/重量级锁,自旋锁,挂起等待锁,普通互斥锁,读写锁,公不公平锁,可不可重入锁,synchronized加锁三阶段过程,锁消除,锁粗化
|
1月前
|
供应链 安全 NoSQL
PHP 互斥锁:如何确保代码的线程安全?
在多线程和高并发环境中,确保代码段互斥执行至关重要。本文介绍了 PHP 互斥锁库 `wise-locksmith`,它提供多种锁机制(如文件锁、分布式锁等),有效解决线程安全问题,特别适用于电商平台库存管理等场景。通过 Composer 安装后,开发者可以利用该库确保在高并发下数据的一致性和安全性。
39 6
|
2月前
|
运维 API 计算机视觉
深度解密协程锁、信号量以及线程锁的实现原理
深度解密协程锁、信号量以及线程锁的实现原理
50 2
|
2月前
|
Java 应用服务中间件 测试技术
Java21虚拟线程:我的锁去哪儿了?
【10月更文挑战第8天】
49 0
|
2月前
|
安全 调度 数据安全/隐私保护
iOS线程锁
iOS线程锁
31 0
|
2月前
|
Java API
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
43 0
|
2月前
|
安全 Java 程序员
【多线程-从零开始-肆】线程安全、加锁和死锁
【多线程-从零开始-肆】线程安全、加锁和死锁
56 0
|
2月前
|
安全 Linux
Linux线程(十一)线程互斥锁-条件变量详解
Linux线程(十一)线程互斥锁-条件变量详解
|
3月前
|
存储 算法 Java
关于python3的一些理解(装饰器、垃圾回收、进程线程协程、全局解释器锁等)
该文章深入探讨了Python3中的多个重要概念,包括装饰器的工作原理、垃圾回收机制、进程与线程的区别及全局解释器锁(GIL)的影响等,并提供了详细的解释与示例代码。
40 0