文章目录
根本性问题
- 1、要充分利用多核 cpu 的性能
- 2、主内存 、工作内存
- 3、cpu 原语执行时 不能被打断
- 4、一条 java 代码并不对应一条 cpu 指令,一条 jvm 字节码也不对应一条 cpu 指令
- 5、汇编代码
- 6、compiler 指令重排序
线程安全典型现象
- 程序故障呈现偶发性
thread
- 启动线程3中方式:继承thread ;实现runnable;使用线程池;(或使用lamba表达式)
- t1 中调用 t2.join ,t2结束,再继续运行t1
- sleep 完会回到【就绪-ready】状态
- yield 让出cpu 一下,回到等待队列【ready】,具体别的线程能不能抢到cpu看情况
- runnable 实际操作系统包括:ready,running 状态,
- ready ——>被线程调度器选中执行——>running
- stop 不要stop
synchronized
-
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
mark-word
来自 《周志明深入理解 java 虚拟机…》
锁升级;锁优化
-
升级
- markword 记录这个线程ID (偏向锁)
- 如果线程争用:升级为 自旋锁
- 自旋10次以后,升级为重量级锁 - OS
-
场景
- 执行时间短(加锁代码),线程数少,用自旋
- 执行时间长,线程数多,用系统锁
-
锁粒度
- 细化:减少锁定的代码,一般情况下会提高效率
- 粗化:增大锁范围;情况是有大量的 小范围锁 ,反而会导致资源竞争比较频繁,不如加把大锁。例如:数据库行锁,表锁:如果每行数据都有一个锁,那还不如在表上加一个锁,反而只要检查表上有没有锁,减少了计算(cpu 时间占用),减少内存占用(存放锁的位置)
volatile
- 1、保证线程可见性
- MESI
- 缓存一致性协议
- 2、禁止指令重排序
-
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
-
AtomicXXX
- 操作可保证原子性
- 源码使用了 unsafe 类
- unsafe 类使用了 cas 操作
unsafe
- 可直接申请内存:allocateMemory;putXX;freeMemory;pageSize
- 直接生成类实例:allocateInstance
- 直接操作类或实例变量:objectFieldOffset;getInt;getObject
- cas 相关操作:compareAndSwapObject ,Int ,Long
- 不同 jdk 版本这个类会有较大变动
CAS
- 无锁优化;自旋
- compare and swap / set
- cas 可以理解成一个函数:
cas (Value, Expected, NewValue)
if V == E
V = N
else
try again or fail
- cas 是 cpu 原语支持的,意思就是上面的一段逻辑执行时,中间不会插入其他指令,也不能被打断
- cas 指令:
- 测试并设置 (Test-and-Set)
- 获取并增加(Fetch-and-Inicrement)
- 交换(Swap)
- 比较并交换(Conpare-and-Swap,下文称CAS)
- 加载链接/条件存储(Load-Linked/Store:Conditional,下文称 LL/SC)
- 其中,前面的3 条是20世纪就已经存在于大多数指令集之中的处理器指令;后面的两条是现代处理器新增的,而且这两条指令的目的和功能是类似的。
- 在IA64、x86指令集中有:
cmpxchg
指令完成CAS功能,在 sparc-TSO 也有 casa 指令实现;而在 ARM 和 PowerPC 架构下,则需要使用一对ldrex/strex
指令来完成LL/SC
的功能 - cas 指令是整颗 cpu(processor) 级别的,不是 cpu 内的核心(core)级别,否则 core1 和 core2 上 分别运行 t1和 t2 线程,都可以访问缓存行里的数据,那么就不能保证原子性
- 底层实现:
- 还是锁:锁数据总线,或者锁缓存行
- 无锁优化 ,是从进程角度和 os 角度看的
- 具体实现描述:
- 首先明白,cpu 取数据命中 缓存行 就不会去 内存中取了,程序局部性原理发挥作用
- Pentium及Pentium之前的处理器中,带有
lock
前缀的指令在执行期间会锁住总线,使得其他处理器暂时无法通过总线访问内存。很显然,这会带来昂贵的开销。 - 从Pentium 4,Intel Xeon及P6处理器开始,intel在原有总线锁的基础上做了一个很有意义的优化:如果要访问的内存区域(area of memory)在
lock
前缀指令执行期间已经在处理器内部的缓存中被锁定(即包含该内存区域的缓存行当前处于独占或已修改状态),并且该内存区域被完全包含在单个缓存行(cache line)中,那么处理器将直接执行该指令。 - 由于在指令执行期间该缓存行会一直被锁定,其它处理器无法读/写该指令要访问的内存区域,因此能保证指令执行的原子性。这个操作过程叫做缓存锁定(cache locking)
- 缓存锁定将大大降低lock前缀指令的执行开销,但是当多处理器之间的竞争程度很高或者指令访问的内存地址未对齐时,仍然会锁住总线。
- 缺点:若一直自旋会有较大的开销
cas aba 问题
- 对于 Integer Long 等这种基础数据类型的数据没啥影响
- 如果是个引用类型就可能出问题,比如:hash表,链表,表头指针没变,但内容变了;你品品这感觉,再细品,吓人不。cpu 不可能去锁整个链表
- 这个问题是从 应用进程的角度看的
- 解决:使用 version / stamped 判断原来的 A 还是不是那个A
LongAdder
- 也是用来实现自增操作,保证原子性
increment()
;longValue()
- 实现是使用了 分段加锁, 也是cas 操作
- 几个数组,分别加锁,分别增加值,这样就可以使得有几个数组就有几个线程并发目前想到的是 数组个数=cpu逻辑核心数可能效果好),最后几个数组的值进行 sum 操作
- 性能上 在比较高的并发下:LongAdder > AtomicXX > synchronized 。 其实具体真的要看并发数
ReentrantLock
- 可重入锁
- 类似于 synchronized,可替代 synchronized
- 也是使用了 cas + AQS
- 与 synchronized 区别:
- 可设置获取锁尝试等待的 时间
- 可以对线程interrupt方法做出响应,在一个线程等待锁的过程中,可以被打断。synchronized 不能被打断,是不可中断锁
- 可设定 是否 公平.就是讲究先来后到 ,后来的线程要检查前面是否有线程在等待,有的话会让前面的线程优先获取锁,但是并不是说前面的线程一定先获取到锁。synchronized 是不公平的 锁
- ReentrantLock.lockInterruptibly() 允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException。
- ReentrantLock.lock方法不允许Thread.interrupt中断,即使检测到Thread.isInterrupted,一样会继续尝试获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为interrupted状态,然后再中断线程。
- condition
- 可以认为 几个 condition 就是 几个队列调用
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.
- 可以认为 几个 condition 就是 几个队列调用
- API
- 1、ReentrantLock lock = new ReentrantLock(); … try{ lock.lock() ;lock.isLocked() } finally { lock.unlock() }
- 2、lock.tryLock() ; tryLock(long time, TimeUnit unit),tryLock() 有返回值 (boolean) 代表是否获取到锁
- 3、lockInterruptibly() 方法,
- 4、 private static ReentrantLock lock=new ReentrantLock(
true
); //参数为true表示为公平锁 - 5、Condition consumer = reentrantLock.newCondition(); Condition producer = reentrantLock.newCondition();
java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire (int acquires)
大致逻辑:- getState() 为 0 则使用 cas 设置 state 为 1;如果成功设置当前线程独占状态,否则获取锁失败
- 如果过不为 1 ;判断持有锁的线程是否为当前线程,如果是则给 state +1(重入/代表获取锁的次数,这里有个溢出判断),否则获取锁失败
- compareAndSetState()
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();
}
- NonfairSync 类继承结构
CountDownLatch
- 倒数 5 、4、3、2、1;然后程序继续运行,类似于 join();比 join(); 更为灵活
- countDownLatch.countDown(); … countDownLatch.await();
CyclicBarrier
- 可循环使用,设置线程个数,等达到指定的个数时继续运行
- 多个线程一同请求一个大的资源,所有线程请求完成后,一起拼接数据;多个线程批量处理数据库历史数据
- API
CyclicBarrier barrier = new CyclicBarrier(20);
CyclicBarrier barrier = new CyclicBarrier(20, Runnable barrierAction new Runnable(){
@Override
public void run(){ sout("人齐了,开始");}
});
// 线程调用 await() 表示自己已经到达栅栏
barrier.awit(); // 不满 20 线程就等在这
CyclicBarrier 与 CountDownLatch 区别
- CountDownLatch 是一次性的,CyclicBarrier 是可循环利用的
- CountDownLatch 参与的线程的职责是不一样的,有的在倒计时,有的在等待倒计时结束。CyclicBarrier 参与的线程职责是一样的。
Phaser
- 阶段栅栏、 阶段器
- @since 1.7
- 相当于连续多个 cyclicBarrier ,所有注册的线程都执行完上一个阶段就进入下一个阶段
- 一个阶段所有线程执行完毕会自动调用
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
- 共享锁、排他锁
- 读锁共享、写锁排他,可提高读取速度,提高并发度
- API
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
- 信号量,只允许几个 同时操作
- 可指定个数;可设定是否公平,并不是一定先来的就先获得
- 典型: 用于 限流
- 有五个卖票窗口,多于5个人后就需要等待,但是不一定要排队,也不定会按排队的顺序来
- API
Semaphore semaphore = new Semaphore(int permits, boolean fair);
Semaphore semaphore = new Semaphore(int permits);
// acquire 调用一次总令牌会少一个 令牌
semaphore.acquire();
Exchanger
- 一般用于两个线程交换变量,可交换局部变量 (使用引用嘛)
- 典型应用:游戏两个人交换装备
- 调用
exchange()
程序会阻塞等到第二个线程到达后交换后继续执行 - 可设定 等待交换的时间
exchange(V x, long timeout, TimeUnit unit)
- API
Exchanger<String> exchanger = new Exchanger<>();
t1:
exchanger.exchange();// 这时候会进行阻塞等待
...
t2:
exchanger.exchange();// 交换后两个线程都继续执行
...
LockSupport
- LockSupport 是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,当然阻塞之后肯定得有唤醒的方法
- 可以先调用
unpark(t)
那么在执行到park()
时就不会再阻塞:- 1、unpark调用时,如果当前线程还未进入park,则许可为true
- 2、park调用时,判断许可是否为true,如果是true,则继续往下执行;如果是false,则等待,直到许可为true
unpark(t)
暂停指定的线程t
,要保证t
已经启动,否则无法保证次操作有任何效果- 对比
wait()
和notify()
- 1、park不需要获取某个对象的锁
- 2、因为中断的时候park不会抛出InterruptedException异常,所以需要在park之后自行判断中断状态,然后做额外的处理
- 3、
park
和unpark
可以实现类似wait
和notify
的功能,但是并不和wait
和notify
交叉,也就是说unpark
不会对wait
起作用,notify
也不会对park
起作用。 - 4、
park
和unpark
的使用不会出现死锁的情况
- API
LockSupport.park();
LockSupport.unpark( Thread t);
StampedLock
wait notify sleep
- notify 不释放锁,只是通知别的线程可以继续了
- wait 释放锁,并等待 notify
- sleep 不释放锁
AQS(CLH)
-
AbstractQueuedSynchronizer : since jdk 1.5
-
关键点:双向链表;state;cas 操作链表
-
volatile state
: 不同的实现这个值 代表不同的含义,例如:ReentrantLock 代表了重入次数 -
用 CAS 操作一个双向链表 ,操作的是每个 node 的地址,比较地址就可以判断是否有其他线程修改值。得不到锁则一直自旋 for 死循环
-
模板方法:设置好几个属性和方法具体的实现类进行自己具体的操作;父类默认实现子类具体实现
-
源码
ThreadLocal
- 实际上有 一个 Map 被 Thread 持有,当前线程 set 、get 操作的都是 这个线程私有的 Map
- 内存泄漏:有内存已经脱离管理了,没有线程可以访问到
- 内存溢出:内存不够用了
- Entry 使用弱引用指向 栈中的 tl :如果使用强引用,即使tl=null ,但是 key 的引用依然指向 堆中 ThreadLocal 对象,而一个生成级的应用中,线程是复用的,不会退出(即使出现异常也会进行相应处理防止线程退出)。但是 tl 实际已经没什么用了,这就产生了内存泄漏
- value 不及时 remove 会导致内存泄漏:key 置成 null ,则与其对应的 value 无法被访问,如果没有在程序中手动调用
remove()
清理,就会发生内存泄漏
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
VarHandle
- 相当于指向任意变量的引用
- 与对象中普通引用相比较的特点:
- 1、对类的普通属性进行原子性操作
- 2、比反射快,直接操作二进制码(?)
- demo
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);
}
}
强软弱虚
- 软引用:SoftReference ,内存空间一旦不足就会回收。软引用非常适合 作缓存使用
- 弱引用:WeakReference,发生 gc 就会回收
- 虚引用:PhantomReference,其中的对象无法被访问,用在堆外内存的回收。对象被回收时 通过
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();
}
}
}
JMH
- java microbenchmark harness
- 2013年首发;由JIT的开发人员开发;归于 openjdk
- 使用jmh:设置jmh依赖;使用 GenerateMicroBenchMark 注解
- 加入maven依赖;下载jmh插件;配置(enable annoation processing);配置pom.xml ;测试
- 官网: http://openjdk.java.net/projects/code-tools/jmh/
- jetbrain 插件地址:https://plugins.jetbrains.com/plugin/7529-jmh-plugin (搜索插件地址:https://plugins.jetbrains.com/idea)
- 启用注解: > compiler -> Annotation Processors -> Enable Annotation Processing
- 错误:
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)
- 这个错误是因为JMH运行需要访问系统的TMP目录,解决办法是: 打开 RunConfiguration -> Environment Variables -> include system environment viables
JMH中的基本概念
- Warmup: 预热,由于JVM中对于特定代码会存在优化(本地化),预热对于测试结果很重要
- Mesurement: 总共执行多少次测试
- Threads: 线程数,由fork指定
- Benchmark mode:基准测试的模式
- Benchmark: 测试哪一段代码
- 官方样例: http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/
Disruptor
- 分裂、瓦解
- 一个线程每秒处理600万订单
- 2011 duke 奖
- 速度最快的 单机 MQ:
- 性能极高,无锁cas,单机支持高并发
- 使用环形 buffer ,直接覆盖(不用清除)旧的数据,降低GC频率;
- 使用一个 pos 变量指向操作的位置;首尾相连使用 % 实现(为了效率更高使用了 位运算 &(size-1))
- 实现了基于事件的生产者消费者模式
- 需要实现:
EventFactory
;EventHandler
源码阅读原则
-
跑不起来不读(程序运行起来是多线程的;程序是多态的,不运行的话有时候会无法进行下去,尤其在读框架源码时)
-
解决问题就好—— 目的性(不然读了半天一团浆糊,最后没什么收获)
-
一条线索到底(只有关键的类需要大面阅读一下)
-
无关细节略过(回头再扣细节)
-
一般不读静态
-
一般动态读法
-
读源码先读骨架
-
读源码很难!理解别人的思路!
-
需要有一定基础
- 1、数据结构基础
- 2、设计模式
demo
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();
}
}
a1b2c3…
-
实现方式有:
-
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();
}
}
- pipedStream
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();
}
}
- ReentrantLock
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();
}
}