Synchronized之外的锁
Synchronized: 系统锁 重量级锁
涉及内容,锁优化,自旋锁(轻量级锁),锁升级
LongAdder类
jdk1.8之后新增类
可以简单认为是AtomicLong的增强版
LongAdder采用的是空间换时间,将热点数据分段,在无并发冲突的时候将操作累加在base上,如果出现了并发冲突,会创建cell类型的数组,不同线程命中到数组的不同卡槽内,每个线程会以CAS形式更新自己卡槽内的value值,最后再将base值和每个cell值求和,降低了CAS失败的概率。提高了效率。
ReentrantLock锁
也是可重入锁
ReentrantLock 可以 替代 Synchronized.
使用方法:
try中lock(), finally中unlock()
ReentrantLock和Synchronized的区别 黄色就是ReentrantLock的优势
- ReentrantLock是手动加锁解锁,Synchronized是自动的。比较灵活
- synchronized如果拿不到锁就阻塞wait,但ReentrantLock有tryLock方法,可以尝试拿 锁。
使用方法
注意:这里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(); }
- 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(乐观读)
- 不可重入锁
- 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