java锁
Lock锁
手动挡锁,手动加锁放锁 Lock lock = new ReentrantLock 是可重入锁。解锁使用try块中的finally更为合适lock.uplock
Condition con = ReentrantLock.newCondition()等待队列,等待拿锁,获得锁 Lock.lock 释放锁 Lock.uplock。
con.await()等于wait()con.signal()等于notify()唤醒线程
线程阻塞 LcokSupport.park() 不需要锁直接阻塞 ; 唤醒线程 LcokSupport.unpark(线程名字)随意唤醒(相当于如果阻塞给当前线程一个0标识他 就阻塞了,唤醒就是把0该成1,可以提前该,这样他就不阻塞了), 普通的阻塞等待,是在一个队列中进行等待的,很难指定某某线程醒过来
线程门栓 CountDownLatch(N)让一个线程去等待N个线程执行完,每执行完一个线程(线程最后一个方法是 CountDownLatch.countDown())N就会减一
在等待线程上需要加一个CountDownLatch.await(),这个方法表示,我一直阻塞需要等CountDown Latch(N)中的N变为0是开始执行
Volatile()
保证线程可见性:
线程1,线程2,线程3,更改一个变量会放到公共区域里面去,修改后的值,并让线程高速缓存中的变量失效,
如果要操作变量,就需要去主存中取
禁止指令重排序:
通过内存屏障实现,volatile会在每一步操作的后面加上一个屏障,让下一步操作看不到,这样虚拟机就不会进行重排,那么指令就会挨个执行
指令重排例子,假如你创建一个单例模式,双重验证,如果此单例模式没有使用Volatie()
当线程1进来的时候第一道验证发现单列对象没有被创建,那么就拿锁开始创建对象,
(创建对象分3步,第一步申请空间创建对象,第二部初始化,第三部指向堆内存,
本来指向堆内存这个操作是第三步执行的,但是虚拟机发现,你有没有初始化跟我指向堆内存有什么关系,于是就可能会发生1,3,2)
线程2进来的时候,线程一没有赋上值, 且线程之间不可见,也就是说线程二不知道线程一已经创建好单例对象了,所以线程二也会去抢锁,
线程二在得到锁之后,发现已经有对象了,就不走if语句,直接拿着这个对象用,但此时线程一只走了1,3这两步,那么线程二从对象中什么都拿不到
(关于线程中变量的自增)
CAS(AtomicLong(要修改的值))可以做自增操作,无锁也能保证线程安全;Atomic包
LongAdder:分段锁,一万个线程的话,给你分成4份锁,最后把结果加起来
CyclicBarriber:相当于一个栅栏满多少线程给你放行
这是一个线程
Runnable maaa = -> System.out.println("计算总帐");
CyclicBarriber cyclicBarriber = new CyclicBarriber(3,maaaa);
这就代表我(maaaa)要等 3 个线程执行完我在开始执行
或者
也可以这样说,什么时候线程等待的有数量到3个了,我(maaaa)在开始执行
线程1,2,3,最后一个方法是 cyclicBarriber.await();
在主线程里调用 cyclicBarriber.reset(),代表我(maaa)在需要 3 个线程,我在执行一遍
//我有一百个线程但是我只允许2个线程同时运行,第二个参数公平的(公平就是不允许插队,轮到谁谁运行)
Semaphore s = new Semaphore(2,true);
s.acquire()s.release()
读写锁
static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//读锁
static Lock reafLock = readWriteLock.readLock();
//写锁
static Lock writeLock = readWriteLock.writeLock();