wait-notify
wait被阻塞后其是会释放锁的,并且需要在sychronized范围内使用
sychronized(lock){
while(!条件){
lock.wait();
}
}
sychronized(lock){
lock.notifyAll();
}
join 模式保护性暂停
public final synchronized void join(final long millis)
throws InterruptedException {
if (millis > 0) {
if (isAlive()) {
final long startTime = System.nanoTime();
long delay = millis;
do {
wait(delay);
} while (isAlive() && (delay = millis -
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
}
} else if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
throw new IllegalArgumentException("timeout value is negative");
}
}
LockSupport park()&&unpark(thread)
Unsafe中定义的,主要是通过判断_counter()是否为一,当调用unpark(thread)时,会将_count()设置为一,当调用park()时,会判断_count(),如果为一则放行,并设置_counter()为0,否则阻塞当前线程
每一个线程都会一个parker对象,由_counter、_cond、_mutex组成
Thread thread = new Thread(() -> {
System.out.println("线程开始");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程暂停");
LockSupport.park();
System.out.println("线程执行完");
});
thread.start();
Thread.sleep(1000);
System.out.println("执行唤醒");
LockSupport.unpark(thread);
线程活跃性
线程活跃性主要是死锁、活锁、饥饿
线程尝试去获取多把锁时出现的问题
活锁:线程之间相互改变对方的结束条件
饥饿:低优先级的线程无法获取锁
ReentrantLock
可重入 可打断 锁超时 可设置为公平锁
解决死锁,使用ReentrantLock中的trylock解决死锁问题
公平锁:谁先进入阻塞队列,谁先得到cpu时间片,可以使用new ReentrantLokc(true)使用,公平锁会降低并发度
reentrantLock.await()相对于thread.wait()其支持多个条件变量,可操作性更好
基础代码块
lock.lock();
try{
}finally{
lock.unlock();
}
可重入
private static ReentrantLock reentrantLock =new ReentrantLock();
public static void main(String[] args) {
reentrantLock.lock();
try {
System.out.println("main");
t1();
}finally {
reentrantLock.unlock();
}
}
private static void t1(){
reentrantLock.lock();
try {
System.out.println("t1");
}finally {
reentrantLock.unlock();
}
}
可打断
Thread t = new Thread(() -> {
try {
reentrantLock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("没有获取锁");
reentrantLock.unlock();
return;
}
try {
System.out.println("获取到了锁");
} finally {
reentrantLock.unlock();
}
});
t.start();
reentrantLock.lock();
Thread.sleep(2000L);
t.interrupt();
blaking
private static boolean isTure =false;
sychronized(this){
if(isTure){
return ;
}
isTure =true;
}
单例模式
public final class Test5 {
public static volatile Test5 t =null;
public static Test5 getTest5() {
if (t!=null){
return t;
}
synchronized (Test5.class){
if (t!=null){
return t;
}
t=new Test5();
return t;
}
}
}
无锁并发
cas+volatile
cas如果cpu核心数够的话不需要上下文切换,sychronized多线程竞争时一定会产生上下文切换
cas是乐观锁,无阻塞,改的时候进行比较,sychronized是悲观锁直接禁止修改
AtomicBoolean AtomicInteger AtomicReference
public static void updateAndSet(AtomicInteger at){
while (true) {
int i1 = at.get();
if (at.compareAndSet(i1, multiNum(i1))) {
break;
}
}
}
public static Integer multiNum(Integer num){
return num *100;
}
ABA问题
为感知其它线程反复修改,可以增加版本号,进行compareandSwap操作是不仅需要对值进行判断也需要对版本号进行判断 只有版本号相等的时候才能进行更新
AtomicStampedReference 版本号
AtomicMarkableReference boolean值
AtomicIntegerArray
AtomicReferenceArray
LoggerAdder
Unsafe
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe o = (Unsafe) theUnsafe.get(null);
long a = o.objectFieldOffset(A.class.getDeclaredField("a"));
long b = o.objectFieldOffset(A.class.getDeclaredField("b"));
A a1 = new A(2, "");
System.out.println(o.compareAndSwapObject(a1, a, 2, 1));
System.out.println(o.compareAndSwapObject(a1, b, "", "aaa"));
System.out.println(a1);
}
private static class A {
private Integer a;
private String b;
public A(Integer a, String b) {
this.a = a;
this.b = b;
}
@Override
public String toString() {
return "A{" +
"a=" + a +
", b='" + b + '\'' +
'}';
}
}
不可变设计
享元模式 对象重复时可以重复使用,最小化内存使用
final定义的时候会有一个写屏障,两个作用,第一个是写屏障之前防止指令重排序,第二是数组完全同步到主存
final获取的时候,其也会进行一个优化,其不会走共享内存中的数据(堆),而是走常量池,或者栈中数据
简单连接池实现
package cn.chuan.xxldemo;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicIntegerArray;
public class Test7 {
public static void main(String[] args) {
Pool pool = new Pool(5);
for (int i = 0; i < 20; i++) {
new Thread(() -> {
try {
Connection conn = pool.getConn();
if (conn == null) {
return;
}
Thread.sleep(2000);
pool.releaseConn(conn);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "thread-" + i).start();
}
}
static class Pool {
//连接个数
int size;
AtomicIntegerArray states;
Vector<Connection> connections;//由于此是不可变的可以使用数组
public Pool(int size) {
this.size = size;
states = new AtomicIntegerArray(size);
connections = new Vector<>(size);
for (int i = 0; i < size; i++) {
this.states.addAndGet(i, 1);
Connection connection = new Connection("连接" + i, i);
this.connections.add(connection);
}
}
//从连接池中获取获取连接
Connection getConn() {
while (true) {
for (int i = 0; i < size; i++) {
if (states.compareAndSet(i, 1, 0)) {
System.out.println("获取coon " + connections.get(i).getName());
return connections.get(i);
}
}
synchronized (this) {
try {
this.wait();
System.out.println("Thread " + Thread.currentThread().getName() + " 正在阻塞");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("线程 " + Thread.currentThread().getName() + "wait被打断");
return null;
}
}
}
}
void releaseConn(Connection connection) {
while (true) {
for (int i = 0; i < size; i++) {
if (states.compareAndSet(i, 0, 1)) {
System.out.println("线程 " + Thread.currentThread().getName() + " 释放了连接" + " coon" + connection.getName());
connections.set(i, connection);
synchronized (this) {
this.notifyAll();
}
return;
}
}
}
}
}
static class Connection {
String name;
Integer id;
public Connection(String name, Integer id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Override
public String toString() {
return "Connection{" +
"name='" + name + '\'' +
", id='" + id + '\'' +
'}';
}
}
}
concurrenthashmap
concurrenthashmap的get()与put()两个操作连起来不是原子的,可以通过concurrenthashmap中computeIfAbsent()方法进行处理,以及集合其它线程安全的类进行处理。
hashmap
jdk1.7的时候其在解决hash冲突后将,将新来的数据加到链表头,会产生死链问题 在扩容的时候产生 jdk1.8之后其将新来的数据加到链表尾可以解决死链问题
concurrenthashmap
ConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel)
concurrentHashMap初始化时有三个参数,其用来定义segment数组大小,当initialCapacity小于concurrencyLevel,则将initialCapacity设置为为concurrencyLevel,计算公式为 (1+initialCapaity/loadFactor),然后再补齐为2的n次方
get()没有加锁
put()默认值时覆盖
1.如果hash数组是否初始化,未初始化先cas创建hash数组
2. hash数组中那个地方是否有冲突,没有冲突则cas创建节点,cas创建节点失败重新创建
3.如果刚好遇到了hash表扩容,则帮助其扩容
4.锁住头节点,然后进行添加元素,然后先判断是否需要扩容,然后再看是否需要将链表转换为2叉树