一、原子变量:
1、AtomicInteger:原子Integer类型
构造:
public AtomicInteger(int initialValue)
public AtomicInteger()
复制代码
方法:
//以原子方式获取旧值并设置新值
public final int getAndSet(int newValue)
//以原子方式获取旧值并给当前值加1
public final int getAndIncrement()
//以原子方式获取旧值并给当前值减1
public final int getAndDecrement()
//以原子方式获取旧值并给当前值加delta
public final int getAndAdd(int delta)
//以原子方式给当前值加1并获取新值
public final int incrementAndGet()
//以原子方式给当前值减1并获取新值
public final int decrementAndGet()
//以原子方式给当前值加delta并获取新值
public final int addAndGet(int delta)
复制代码
例如:incrementAndGet方法以原子方式将变量自增,并返回自增后的值。也就是说,获得值、增1并设置然后生成新值的操作不会中断。
相关原理:
private volatile int value;
复制代码
通过volatile进行修饰,保证内存可见性
其他类型:
- AtomicBoolean:原子Boolean类型
- AtomicLong:原子Long类型
- AtomicReference:原子引用类型 ......
2、AtomicIntegerArray:原子数组类型
构造:
public AtomicIntegerArray(int length) {
array = new int[length];
}
public AtomicIntegerArray(int[] array) {
this.array = array.clone();
}
复制代码
常用方法:
public final boolean compareAndSet(int i, int expect, int update)
public final int getAndIncrement(int i)
public final int getAndAdd(int i, int delta)
复制代码
二、并发容器
1、CopyOnWriteArrayList
特点:
- 它是线程安全的,可以被多个线程并发访问
- 它的迭代器不支持修改操作
- 它以原子方式支持一些复合操作
private transient volatile Object[] elements;
复制代码
通过volatile进行修饰保证内存可见性
存入元素
/**
* 不存在就添加,并返回true
*/
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
addIfAbsent(e, snapshot);
}
private boolean addIfAbsent(E e, Object[] snapshot) {
//通过synchronized实现线程同步
synchronized (lock) {
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]
&& Objects.equals(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;
}
}
复制代码
获取元素:
public E get(int index) {
return get(getArray(), index);
}
private E get(Object[] a, int index) {
return (E) a[index];
}
复制代码
总结:CopyOnWriteArrayList数组通过volatile修饰保证内存可见,写操作通过synchronized实现同步,性能会低一些,读操作不需要同步。适用于多线程操作写操作比较少,读操作比较多的情况。
2、ConcurrentHashMap
特点:
- 线程安全
- 分段锁
- 读不需要加锁
原理:
- ConcurrentHashMap采用分段锁技术。根据哈希值将将数据分为多段,而每个段有一个独立的锁,每一个段相当于一个独立的哈希表。 无论是保存键值对还是根据键查找,都先根据键的哈希值映射到段,再在段对应的哈希表上进行操作。
- 对于写操作,需要获取锁,不能并行,但是读操作可以,多个读可以并行,写的同时也可以读。
三、阻塞队列
1、阻塞队列使用场景:
- 当队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞,直到有数据放入队列。
- 当队列中填满数据的情况下,生产者端的所有线程都会被自动阻塞,直到队列中有空的位置,线程被自动唤醒。
2、常见阻塞队列
- ArrayBlockingQueue:由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue:由链表结构组成的有界阻塞队列。
- PriorityBlockingQueue:支持优先级排序的无界阻塞队列。
- DelayQueue:使用优先级队列实现的无界阻塞队列。
- SynchronousQueue:不存储元素的阻塞队列。
- LinkedTransferQueue:由链表结构组成的无界阻塞队列。
- LinkedBlockingDeque:由链表结构组成的双向阻塞队列。
3、使用:
public class BlockTest{
private ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
public static void main(String[] args) {
BlockTest test = new BlockTest();
Product product = test.new Product();
product.start();
Consume consume = test.new Consume();
consume.start();
}
class Product extends Thread {
@Override
public void run() {
try {
queue.put(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consume extends Thread {
@Override
public void run() {
try {
queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
复制代码
4、原理:
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
......
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
//获取元素
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
//元素被空进行等待
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length) takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
//唤醒其他线程
notFull.signal();
return x;
}
//存储元素
public void put(E e) throws InterruptedException {
Objects.requireNonNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
//元素个数等于数组长度进行等待,当被其他线程唤醒时插入元素
try {
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
private void enqueue(E x) {
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length) putIndex = 0;
count++;
//唤醒其他线程
notEmpty.signal();
}
......
}
复制代码
总结:主要通过ReentrantLock加锁,避免多线程引起的线程安全问题。通过await和signal进行消费者线程和生产者线程的相互切换。
四、同步器:
1、信号量Semaphore//传入参数为许可数
private static Semaphore semaphore = new Semaphore(2);
public static void main(String[] args) {
for(int i = 0; i < 5; i ++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
//获取许可
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " " + new Date());
Thread.sleep(5000l);
//释放许可
semaphore.release();
} catch (InterruptedException e) {
System.err.println(Thread.currentThread().getName() + " interrupted");
}
}
}).start();
}
}
复制代码
限制对资源的并发访问数
2、倒计时门栓CountDownLatch
它相当于是一个门栓,一开始是关闭的,所有希望通过该门的线程都需要等待,然后开始倒计时,倒计时变为0后,门栓打开,等待的所有线程都可以通过。
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(5);
for(int i = 0; i < 5; i ++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " " + new Date() + " run");
try {
Thread.sleep(5000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
}
}).start();
}
try {
//await()检查计数是否为0,如果大于0,就等待
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
复制代码
3、障栅CyclicBarrier
CyclicBarrier阻塞调用的线程,直到条件满足时,阻塞的线程同时被打开。
public static void main(String[] args) {
Random random = new Random();
CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
for(int i = 0; i < 5; i ++) {
new Thread(new Runnable() {
@Override
public void run() {
int secs = random.nextInt(5);
System.out.println(Thread.currentThread().getName() + " " + new Date() + " run, sleep " + secs + " secs");
try {
Thread.sleep(secs * 1000);
//调用await后,表示自己已经到达,如果自己是最后一个到达的,就执行可选的命令,执行后,唤醒所有等待的线程
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + new Date() + " runs over");
}
}).start();
}
}
复制代码