多线程相关类(锁常用类) ReentrantLock, ReadWriteLock读写锁等

Synchronized之外的锁

Synchronized: 系统锁 重量级锁
涉及内容,锁优化,自旋锁(轻量级锁),锁升级

LongAdder类

jdk1.8之后新增类
可以简单认为是AtomicLong的增强版
LongAdder采用的是空间换时间,将热点数据分段,在无并发冲突的时候将操作累加在base上,如果出现了并发冲突,会创建cell类型的数组,不同线程命中到数组的不同卡槽内,每个线程会以CAS形式更新自己卡槽内的value值,最后再将base值和每个cell值求和,降低了CAS失败的概率。提高了效率。

ReentrantLock锁

也是可重入锁
ReentrantLock 可以 替代 Synchronized.
使用方法:
try中lock(), finally中unlock()

ReentrantLock和Synchronized的区别 黄色就是ReentrantLock的优势

  1. ReentrantLock是手动加锁解锁,Synchronized是自动的。比较灵活
  2. synchronized如果拿不到锁就阻塞wait,但ReentrantLock有tryLock方法,可以尝试拿 锁。
    使用方法
    boolean locked = false;
    		
    try {
    	locked = lock.tryLock(5, TimeUnit.SECONDS);
    	System.out.println("m2 ..." + locked);
    } catch (InterruptedException e) {
    	e.printStackTrace();
    } finally {
    	if(locked) lock.unlock();
    }
    
    注意:这里tryLock() 拿不到锁不是进异常
  3. ReentrantLock还可以设置为公平锁
    ReentrantLock lock=new ReentrantLock(true);
    这里默认参数:false(非公平)
    公平锁就是有一个线程等待的队列,非公平锁上来直接抢,不管有没有队列,公平就是判断有没有队列,有队列就排队。

CountDownLatch

Latch闩锁 countDouwnLatch 倒数的门锁

就是线程执行完了

使用方法

Thread[] threads = new Thread[100];
CountDownLatch latch = new CountDownLatch(threads.length);

for(int i = 0; i < threads.length; i++) {
	threads[i] = new Thread(() -> {
		int result = 0;
		for(int j = 0; j < 10000; j++)
			result += j;
		latch.countDown();
	});
}

for(int i = 0; i < threads.length; i++) {
	threads[i].start();
}

try {
	latch.await();
} catch (InterruptedException e) {
	e.printStackTrace();
}

CyclicBarrier

Barrier 栅栏 CyclicBarrier 循环栅栏

new CyclicBarrier()参数中的第一个参数就是线程数量,就是什么时候线程达到这个数量,就继续执行

CyclicBarrier 和 CountDownLatch的区别:
CountDownLatch是执行完就减一(可以是一个线程多次执行),CyclicBarrier是当线程数达到规定数量时才执行下·

使用方法:

CyclicBarrier barrier = new CyclicBarrier(20, new Runnable() {
	@Override
	  public void run() {
	      System.out.println("满人,发车");
	  }
});

for(int i=0; i<100; i++) {
	new Thread(()->{
	    try {
	        barrier.await();
	    } catch (InterruptedException e) {
	        e.printStackTrace();
	    } catch (BrokenBarrierException e) {
	        e.printStackTrace();
	    }
	}).start();    
}

Phaser

Phase 阶段
类似于一个又一个的barrier
可以解决遗传算法
使用方法:
MarriagePhaser phaser = new MarriagePhaser();
phaser.bulkRegister([中间是自己定义的一共多少个线程]);
phaser.arriveAndDeregister();
phaser.arriveAndAwaitAdvance();
类需要继承Phaser然后重写onAdvance() 方法

ReadWriteLock

顾名思义:读写锁
可以锁读,可以锁写

往死了重要!
共享锁:
共享,就是多个人可以共享一把锁,翻译成人话就是有这个锁的就是自家人,就可以执行。其中读锁就是共享锁,意思就是不同线程读的时候用的锁就是同一把锁,就是读线程拿到锁的时候只要是读线程就可以访问。
排他锁:
写锁就是排他锁,就是自己一个人用。其他写线程和读线程都不能用。(自私)

问:为什么不只给写上锁?
会出现脏读现象

使用方法:

static int number = 123;

    static ReentrantLock lock = new ReentrantLock();

    static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    static Lock readLock = readWriteLock.readLock();
    static Lock writeLock = readWriteLock.writeLock();

    private static void readMethod(Lock lock) {
        try {
            lock.lock();
            Thread.sleep(500);
            System.out.println("read Method! ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    private static void writeMethod(Lock lock, int n) {
        try {
            lock.lock();
            number = n;
            Thread.sleep(500);
            System.out.println("write Method! ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        T10_Exercise t = new T10_Exercise();
        /*Runnable read = () -> readMethod(lock);
        Runnable write = () -> writeMethod(lock, 888);
        for (int i = 0; i < 18; i++) {
            new Thread(read).start();
        }
        for (int i = 0; i < 2; i++) {
            new Thread(write).start();
        }*/

        for (int i = 0; i < 18; i++) {
            new Thread(() -> readMethod(readLock)).start();
        }
        for (int i = 0; i < 2; i++) {
            new Thread(() -> writeMethod(writeLock, 888)).start();
        }
    }

Semaphore

semaphore 信号

使用:
Semaphore s = new Semaphore(permits:2);//也可以设置为公平 true
s.acquire(); //拿到信号量,信号量数据–,减到为零就不能获取了
s.release(); //释放信号量,信号量数据++

使用场景:限流
小问题:
semaphore中permits是可以同时运行的线程一共有多少个,但有多少个线程在线程池中不管。

Exchanger

两个线程中交换数据使用(不同线程中的通信)

static Exchanger<String> exchanger = new Exchanger<>();

public static void main(String[] args) {
   new Thread(()->{
       String s = "T1";
       try {
           s = exchanger.exchange(s);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println(Thread.currentThread().getName() + " " + s);

   }, "t1").start();


   new Thread(()->{
       String s = "T2";
       try {
           s = exchanger.exchange(s);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println(Thread.currentThread().getName() + " " + s);

   }, "t2").start();
}

另外一个读写锁StampedLock(乐观读)

  1. 不可重入锁
  2. ReadWriteLock中读锁是悲观锁,而StampedLock可以使用乐观读锁
    怎么解释悲观:如果有线程正在读,写线程需要等待读线程释放锁后才能获取写锁,即读的过程中不允许写。
    乐观:乐观地认为读的过程中大概率不会有写入

使用方法和概念以下两篇文章讲述的很清楚

https://www.liaoxuefeng.com/wiki/1252599548343744/1309138673991714

https://www.cnblogs.com/zxporz/p/11642176.html

问题:synchrnoized和reentrantlock的底层实现及重入的底层原理?

https://blog.csdn.net/lvxinchun/article/details/107002689

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值