多线程与高并发二
基础知识
volatile关键字保证的是:
- 可见性(缓存一致性),只能保证可见性,不能保证原子性,所以他不能替换synchronized
- 禁止指令重排序(CPU)
指令重排序在单例模式中,体现,双重认证加防止指令重新排序
CAS(无锁优化,自旋,乐观锁)
Compare And Set/Swap
cas(V,Expected,NewValue)-
if V == E
V == New
otherwise try again or fail -
Cpu原语支持
-
ABA问题:加Version A:1.0 B 2.0 A 3.0 然后在CAS操作时候可以校验版本号
如果是基础类型,无所谓
如果是引用类型,例如:你的前女朋友跟你复合,你中间经历了别的女人。
-
基于CAS的新的锁
- ReentrantLock 可重入锁,先看下syn方法的可重入锁,下面事例很好说明了一切
public class T01_ReentrantLock1 {
synchronized void m1() {
for(int i=0; i<10; i++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
if(i == 2) m2(); //syn是可重入锁,syn是可以调用syn方法的,锁是可以重入的
}
}
synchronized void m2() {
System.out.println("m2 ...");
}
public static void main(String[] args) {
T01_ReentrantLock1 rl = new T01_ReentrantLock1();
new Thread(rl::m1).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//new Thread(rl::m2).start();
}
}
CyclicBarrier 是什么
从字面上的意思可以知道,这个类的中文意思是“循环栅栏”。大概的意思就是一个可循环利用的屏障。
它的作用就是会让所有线程都等待完成后才会继续下一步行动。
举个例子,就像生活中我们会约朋友们到某个餐厅一起吃饭,有些朋友可能会早到,有些朋友可能会晚到,但是这个餐厅规定必须等到所有人到齐之后才会让我们进去。这里的朋友们就是各个线程,餐厅就是 CyclicBarrier。
怎么使用 CyclicBarrier(需求)
一个线程组的线程需要等待所有线程完成任务后再继续执行下一次任务
代码
public class CyclicBarrierDemo {
static class TaskThread extends Thread {
CyclicBarrier barrier;
public TaskThread(CyclicBarrier barrier) {
this.barrier = barrier;
}
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(getName() + " 到达栅栏 A");
barrier.await();
System.out.println(getName() + " 冲破栅栏 A");
Thread.sleep(2000);
System.out.println(getName() + " 到达栅栏 B");
barrier.await();
System.out.println(getName() + " 冲破栅栏 B");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
int threadNum = 5;
CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 完成最后任务");
}
});
for(int i = 0; i < threadNum; i++) {
new TaskThread(barrier).start();
}
}
}
ReadWriteLock
1.共享锁
2.排他锁
举个例子:当我们使用ReentrantLock创建锁时候,如下代码:每次去读,去写,相当于每个线程完了以后才能进行下面的线程,但是当我们使用ReadWriteLock lock = new ReetrantReadWriteLock();创建锁时候,读使用lock.readLock();读锁,写的时候使用lock.writeLock();写锁,线程在相同锁的情况下,读可以共享,18个线程相当于1s就可以读完
static Lock lock = new ReentrantLock();
private static int value;
static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
static Lock readLock = readWriteLock.readLock();
static Lock writeLock = readWriteLock.writeLock();
public static void read(Lock lock) {
try {
lock.lock();
Thread.sleep(1000);
System.out.println("read over!");
//模拟读取操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void write(Lock lock, int v) {
try {
lock.lock();
Thread.sleep(1000);
value = v;
System.out.println("write over!");
//模拟写操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}