thread-最佳实践

文章详细介绍了Java并发控制中的wait-notify机制,LockSupport工具类的park()和unpark()方法,以及ReentrantLock的使用,包括可重入性、可打断性和公平锁特性。同时讨论了线程活跃性问题,如死锁、活锁和饥饿,并举例展示了单例模式和无锁并发编程中的CAS操作。还提到了Atomic类在解决ABA问题上的应用和Unsafe的使用。
摘要由CSDN通过智能技术生成

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叉树

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值