synchronized:hotspot 使用 对象头的mark-word 两个bit 实现,虚拟机规范没有规定怎么实现
锁加谁身上都行,锁定的时候锁的是某个对象,不一定是并发操作的那个对象,任意对象都行
synchronized (this)= public synchronized void m() {……}
能同时保证可见性和原子性
是可重入的锁:o 这个对象在一个线程里可以 反复调用 m() 方法
synchronized(T.class) 会锁住 T 的 class 对象,
class 对象由 classLoader 从 class 文件装载,一个 classLoader 只能装载一次某个 class 如:T;不同 classLoader 可以装载再次装载T的 class 对象,但是他们之间不是互通的
异常的发生会解除锁占用,这时候可能会出现程序乱入,使其他线程读到不应读到的数据
锁实现的进化,直接使用os 锁,随着java 更多应用在高并发场景,锁进化成 偏向锁 自旋锁 重量级锁(os 锁)
不能用于 String (常量池);Integer 、Long (这两种对象,val值一旦改变就会新new 一个对象,就锁不住了)
锁 null
升级
场景
锁粒度
DCL 单例
double check lock
如上单例代码,在不使用 volatile 时由于指令重排序的存在,会导致 INSTANCE = new Mgr06();
中 INSTANCE
被其他线程读到不希望的值,超高并发时可能出现异常情况,如:淘宝秒杀
因为 new Mgr06();
的 bytecode 指令并不是一条(3条及以上),更不提汇编级别的指令
new 对象的3步大概是:1)申请内存;2)给成员变量赋值;3)栈指针指向此新对象
如上3步如果2)、3)步进行了指令重排序,那么成员变量还没赋值,可能会导致 例如 Integer 、String 这种默认值被读取
结论:dcl 单例 volatile 是必须的
loadfence 原语指令
storefence
cas (Value, Expected, NewValue)
if V == E
V = N
else
try again or fail
cmpxchg
指令完成CAS功能,在 sparc-TSO 也有 casa 指令实现;而在 ARM 和 PowerPC 架构下,则需要使用一对 ldrex/strex
指令来完成 LL/SC
的功能lock
前缀的指令在执行期间会锁住总线,使得其他处理器暂时无法通过总线访问内存。很显然,这会带来昂贵的开销。lock
前缀指令执行期间已经在处理器内部的缓存中被锁定(即包含该内存区域的缓存行当前处于独占或已修改状态),并且该内存区域被完全包含在单个缓存行(cache line)中,那么处理器将直接执行该指令。increment()
; longValue()
producer.await()
就进入producer
队列producer.signalAll()
叫醒这个队列中等待的线程signalAll()
jdk 解释: Wakes up all waiting threads.If any threads are waiting on this condition then they are all woken up. Each thread must re-acquire the lock before it can return from await.true
); //参数为true表示为公平锁java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire (int acquires)
大致逻辑:
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireQueued (final Node node, int arg)
java.util.concurrent.locks.AbstractQueuedSynchronizer#addWaiter(Node mode)
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire
源码 public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
CyclicBarrier barrier = new CyclicBarrier(20);
CyclicBarrier barrier = new CyclicBarrier(20, Runnable barrierAction new Runnable(){
@Override
public void run(){ sout("人齐了,开始");}
});
// 线程调用 await() 表示自己已经到达栅栏
barrier.awit(); // 不满 20 线程就等在这
onAdvance(int phase, int registerParties)
方法。 其中 phase 默认从 0 开始MyPhaser extends Phaser {
@Overwrite
onAdvance(int phase, int registerParties ){
switch(phase){
case 0:
break;
case 1
...
}
}
}
phaser.bulkRegister(5);
...
phaser.arriveAndAwaitAdvance()
phaser.arriveAndDeRegsiter();
phaser.addRegister();
phaser.doRegister();
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
Lock readLock = readWriteLock .readLock();
Lock writeLock = readWriteLock .writeLock();
...
read(){
try {
readLock .lock();
}finally{
readLock.unlock()
}
}
...
write(){
try {
writeLock .lock();
}finally{
writeLock .unlock()
}
}
Semaphore semaphore = new Semaphore(int permits, boolean fair);
Semaphore semaphore = new Semaphore(int permits);
// acquire 调用一次总令牌会少一个 令牌
semaphore.acquire();
exchange()
程序会阻塞等到第二个线程到达后交换后继续执行exchange(V x, long timeout, TimeUnit unit)
Exchanger<String> exchanger = new Exchanger<>();
t1:
exchanger.exchange();// 这时候会进行阻塞等待
...
t2:
exchanger.exchange();// 交换后两个线程都继续执行
...
unpark(t)
那么在执行到 park()
时就不会再阻塞:
unpark(t)
暂停指定的线程 t
,要保证 t
已经启动,否则无法保证次操作有任何效果wait()
和 notify()
park
和 unpark
可以实现类似 wait
和 notify
的功能,但是并不和wait
和notify
交叉,也就是说unpark
不会对wait
起作用,notify
也不会对park
起作用。park
和unpark
的使用不会出现死锁的情况LockSupport.park();
LockSupport.unpark( Thread t);
AbstractQueuedSynchronizer : since jdk 1.5
关键点:双向链表;state;cas 操作链表
volatile state
: 不同的实现这个值 代表不同的含义,例如:ReentrantLock 代表了重入次数
用 CAS 操作一个双向链表 ,操作的是每个 node 的地址,比较地址就可以判断是否有其他线程修改值。得不到锁则一直自旋 for 死循环
模板方法:设置好几个属性和方法具体的实现类进行自己具体的操作;父类默认实现子类具体实现
源码
remove()
清理,就会发生内存泄漏static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
public class T01_HelloVarHandle {
int x = 8;
private static VarHandle handle;
static {
try {
handle = MethodHandles.lookup().findVarHandle(T01_HelloVarHandle.class, "x", int.class);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
T01_HelloVarHandle t = new T01_HelloVarHandle();
//plain read / write
System.out.println((int)handle.get(t));
handle.set(t,9);
System.out.println(t.x);
handle.compareAndSet(t, 9, 10);
System.out.println(t.x);
handle.getAndAdd(t, 10);
System.out.println(t.x);
}
}
ReferenceQueue
可以检测到,从而回收堆外内存
虚引用和弱引用对关联对象的回收都不会产生影响,如果只有虚引用 或 弱引用关联着对象,那么这个对象就会被回收。它们的不同之处在于弱引用的get方法,虚引用的get方法始终返回null,弱引用可以使用ReferenceQueue,虚引用必须配合ReferenceQueue使用。
jdk中直接内存的回收就用到虚引用,由于jvm自动内存管理的范围是堆内存,而直接内存是在堆内存之外(其实是内存映射文件,自行去理解虚拟内存空间的相关概念),
所以直接内存的分配和回收都是有Unsafe类去操作,java在申请一块直接内存之后,会在堆内存分配一个对象保存这个堆外内存的引用,这个对象被垃圾收集器管理,一旦这个对象被回收,相应的用户线程会收到通知并对直接内存进行清理工作。
事实上,虚引用有一个很重要的用途就是用来做堆外内存的释放,DirectByteBuffer就是通过虚引用来实现堆外内存的释放的。
import java.lang.ref.SoftReference;
public class T02_SoftReference {
public static void main(String[] args) {
SoftReference<byte[]> m = new SoftReference<>(new byte[1024*1024*10]);
//m = null;
System.out.println(m.get());
System.gc();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(m.get());
//再分配一个数组,heap将装不下,这时候系统会垃圾回收,先回收一次,如果不够,会把软引用干掉
byte[] b = new byte[1024*1024*15];
System.out.println(m.get());
}
}
---
import java.lang.ref.WeakReference;
public class T03_WeakReference {
public static void main(String[] args) {
WeakReference<M> m = new WeakReference<>(new M());
System.out.println(m.get());
System.gc();
System.out.println(m.get());
ThreadLocal<M> tl = new ThreadLocal<>();
tl.set(new M());
tl.remove();
}
}
---
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.LinkedList;
import java.util.List;
public class T04_PhantomReference {
private static final List<Object> LIST = new LinkedList<>();
private static final ReferenceQueue<M> QUEUE = new ReferenceQueue<>();
public static void main(String[] args) {
PhantomReference<M> phantomReference = new PhantomReference<>(new M(), QUEUE);
static class M{}
new Thread(() -> {
while (true) {
LIST.add(new byte[1024 * 1024]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
System.out.println(phantomReference.get());
}
}).start();
new Thread(() -> {
while (true) {
Reference<? extends M> poll = QUEUE.poll();
if (poll != null) {
System.out.println("--- 虚引用对象被jvm回收了 ---- " + poll);
}
}
}).start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
ERROR: org.openjdk.jmh.runner.RunnerException: ERROR: Exception while trying to acquire the JMH lock (C:\WINDOWS\/jmh.lock): C:\WINDOWS\jmh.lock (拒绝访问。), exiting. Use -Djmh.ignoreLock=true to forcefully continue.
at org.openjdk.jmh.runner.Runner.run(Runner.java:216)
at org.openjdk.jmh.Main.main(Main.java:71)
EventFactory
;EventHandler
跑不起来不读(程序运行起来是多线程的;程序是多态的,不运行的话有时候会无法进行下去,尤其在读框架源码时)
解决问题就好—— 目的性(不然读了半天一团浆糊,最后没什么收获)
一条线索到底(只有关键的类需要大面阅读一下)
无关细节略过(回头再扣细节)
一般不读静态
一般动态读法
读源码先读骨架
读源码很难!理解别人的思路!
需要有一定基础
package com.common.cn.juc;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Container<T> {
private LinkedList<T> list = new LinkedList<>();
private int MAX = 10;
private int count =0;
ReentrantLock reentrantLock= new ReentrantLock();
Condition consumer = reentrantLock.newCondition();
Condition producer = reentrantLock.newCondition();
public void put(T t){
try {
reentrantLock.lock();
while (count == MAX){
try {
producer.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(t);
++count;
consumer.signalAll();
} finally {
reentrantLock.unlock();
}
}
public T get(){
T t = null;
try {
reentrantLock.lock();
while (count <= 0){
try {
consumer.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
t = list.removeFirst();
--count;
producer.signalAll();
} finally {
reentrantLock.unlock();
}
return t;
}
public int getCount(){
return count;
}
public static void main(String[] args) {
Container<String> container = new Container<>();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true){
System.out.println(container.get());
}
},"c" + i);
thread.start();
}
for (int i = 0; i < 2; i++) {
Thread thread = new Thread(()->{
for (int j = 0; j < 5; j++) {
container.put(Thread.currentThread().getName() + "#" + j);
System.out.println( "==>>size " + container.getCount());
}
},"p-" + i);
thread.start();
}
/*new Thread(()->{
while (true){
System.out.println(continer.getCount());
}
},"p-c").start();*/
}
}
---
package com.common.cn.juc;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class TaoBaoThread {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
CountDownLatch countDownLatch = new CountDownLatch(1);
CountDownLatch countDownLatch2 = new CountDownLatch(1);
Thread t1 = new Thread(()->{
for (int i = 0; i < 10; i++) {
list.add(i+1);
System.out.println(i+1);
if(list.size() == 5){
countDownLatch.countDown();
try {
countDownLatch2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread t2 = new Thread(()->{
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" t2 exit ==>>");
countDownLatch2.countDown();
});
t2.start();
t1.start();
}
}
实现方式有:
LockSupport
synchronized、 wait、notify
ReentrantLock 可以一个,也可以两个 condition
volatile + 自旋
AutomaticInteger + 自旋 :占用cpu时间
BlockingQueue
PipedStream
SynchoronousQueue
TransferQueue : 和SynchronousQueue 类似
SynchoronousQueue
package com.common.cn.collection;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
public class TX01_SynchronousQueue {
static BlockingQueue<Integer> synchronousQueue = new SynchronousQueue<>();
public static void main(String[] args) {
/*char c = (char) 97;
System.out.println(c);*/
Thread t1 = new Thread(() ->{
for (int i = 1; i <= 26; i++) {
System.out.print(i + "_");
try {
synchronousQueue.put(i);
synchronousQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(() ->{
for (int i = 1; i <= 26; i++) {
try {
int take = synchronousQueue.take();
char word = (char)(take + 96) ;
System.out.println(word);
synchronousQueue.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
t2.start();
}
}
package com.common.cn.collection;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class T10_00_PipedStream {
public static void main(String[] args) throws Exception {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
PipedInputStream input1 = new PipedInputStream();
PipedInputStream input2 = new PipedInputStream();
PipedOutputStream output1 = new PipedOutputStream();
PipedOutputStream output2 = new PipedOutputStream();
input1.connect(output2);
input2.connect(output1);
String msg = "Your Turn";
new Thread(() -> {
byte[] buffer = new byte[9];
try {
for(char c : aI) {
input1.read(buffer);
if(new String(buffer).equals(msg)) {
System.out.print(c);
}
output1.write(msg.getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}, "t1").start();
new Thread(() -> {
byte[] buffer = new byte[9];
try {
for(char c : aC) {
System.out.print(c);
output2.write(msg.getBytes());
input2.read(buffer);
if(new String(buffer).equals(msg)) {
continue;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}, "t2").start();
}
}
package com.common.cn.collection;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class T08_00_lock_condition {
public static void main(String[] args) {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(()->{
try {
lock.lock();
for(char c : aI) {
System.out.print(c);
condition.signal();
condition.await();
}
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t1").start();
new Thread(()->{
try {
lock.lock();
for(char c : aC) {
System.out.print(c);
condition.signal();
condition.await();
}
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t2").start();
}
}