Java-第十六部分-JUC-Lock接口、线程间通信和集合的线程安全

192 阅读2分钟

JUC全文

Lock接口

  • lock需要用户手动上锁、释放锁
  • 可以让等待锁的线程响应中断
  • 通过Lock可以知道有没有成功获取锁

多线程编程步骤(上)

  • 创建资源类,创建属性和操作方法
  1. 资源类,以后准备好的功能和资源的集合,封装属性和方法
  2. 高内聚
  • 创建多个线程,调用资源类的操作方法

Synchronized

  • 同步锁
  • 修饰代码块、方法、静态方法、类
  • 自动上锁,释放锁
  • 发生异常时,会自动释放线程占有的锁,不会导致死锁

ReentrantLock

  • 可重入锁,类似排队上厕所
  • 卖票举例
class LTicket {
    int count = 30;
    //创建可重复锁
    private final ReentrantLock lock = new ReentrantLock();
    void sellTicket() {
        try {
            //上锁
            lock.lock();
            //卖票过程
            if (count > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出:" + count-- + "剩下:" + count);
            }
        } finally { //不管有没有异常,都要解锁
            //解锁
            lock.unlock();
        }
    }
}

lock与lockInterruptibly

  • 在阻塞等待获取锁的过程中
  1. lock()不能被t2.interrupt()中断,只有当获取锁后,立即被中断
  2. lockInterruptibly()在阻塞过程中能被中断

线程间通信

多线程编程步骤(中)

  • 创建资源类,创建属性和操作方法
  • 在资源类的操作方法中,要进行判断、执行、通知
  • 创建多个线程,调用资源类方法
  • 防止虚假唤醒问题

sync实现加一减一

  • 存在虚假唤醒问题,在判断时,只会判断一次;用while循环解决

类似坐飞机,下飞机后,再次上飞机还需要做安检

class Shared {
    int number = 0;
    synchronized void incr() throws InterruptedException {
        //判断
        //执行
        //用while,保证每次唤醒,都会经过判断
        if (number != 0) {
            //阻塞
            this.wait();
        }
        number ++;
        System.out.println(Thread.currentThread().getName() + " - " + number);
        //通知
        this.notifyAll();
    }
    synchronized void decr() throws InterruptedException {
        if (number != 1) {
            this.wait();
        }
        number --;
        System.out.println(Thread.currentThread().getName() + " - " + number);
        this.notifyAll();
    }
}

Lock接口实现

class LShared {
    int number = 0;
    //创建lock
    private final ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    void incr() {
        try {
            lock.lock();
            //判断
            while (number != 0) {
                //等待
                condition.await();
            }
            number ++;
            System.out.println(Thread.currentThread().getName() + " - " + number);
            //通知
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    void decr() {
        try {
            lock.lock();
            while(number != 1) {
                //等待
                condition.await();
            }
            number --;
            System.out.println(Thread.currentThread().getName() + " - " + number);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

线程间定制化通信

  • 可以唤醒通过condition1进行等待的线程
condition1.signal();

集合的线程安全

list

  • list集合,add方法没有Synchronized修饰,是线程不安全的

vector

  • 有Synchronized关键字,jdk1.0 image.png

Collections

  • 方法内做了转换
List<String> list = Collections.synchronizedList(new ArrayList<>());
public static <T> List<T> synchronizedList(List<T> list) {
    return (list instanceof RandomAccess ?
            new SynchronizedRandomAccessList<>(list) :
            new SynchronizedList<>(list));
}

CopyOnWriteArrayList

  • 写时复制技术
  1. 支持并发读
  2. 独立写,先复制一份,写入新内容,再将新数组设置回去
List<String> list = new CopyOnWriteArrayList<>();
  • add方法
public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock(); //锁
    try {
        Object[] elements = getArray(); //获取当前数组
        int len = elements.length;
        //复制一份,且长度+1
        Object[] newElements = Arrays.copyOf(elements, len + 1); 
        //增加新内容
        newElements[len] = e;
        //设置回去
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}
  • get
final Object[] getArray() {
    return array;
}
  • set
final void setArray(Object[] a) {
    array = a;
}

HashSet

CopyOnWriteArraySet

Set<String> set = new CopyOnWriteArraySet<>();
  • 构造
public CopyOnWriteArraySet() {
    al = new CopyOnWriteArrayList<E>();
}
  • add
public boolean add(E e) {
    return al.addIfAbsent(e);
}
  • addIfAbsent
public boolean addIfAbsent(E e) {
    Object[] snapshot = getArray();
    //判断是否存在,如果存在就不可添加
    return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
        addIfAbsent(e, snapshot);
}
  • 防止判断过程中,其他线程修改了set
private boolean addIfAbsent(E e, Object[] snapshot) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] current = getArray();
        int len = current.length;
        //进行比较
        if (snapshot != current) {
            // Optimize for lost race to another addXXX operation
            int common = Math.min(snapshot.length, len);
            for (int i = 0; i < common; i++)
                if (current[i] != snapshot[i] && eq(e, current[i]))
                    return false;
            if (indexOf(e, current, common, len) >= 0)
                    return false;
        }
        Object[] newElements = Arrays.copyOf(current, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

HashMap

ConcurrentHashMap

Map<String, String> map = new ConcurrentHashMap<>();
  • put方法中使用的是synchronized image.png