1、先聊聊CAS
CAS即compare and swap,原子包中很多地方使用了该原子操作(本来这个cas包含多个操作,(1)取出要操作对象的值V (2)与线程中该操作对象的旧值作对比VOld (3)如果V = VOld则替换为新值VNew)。
CAS是cpu指令级别的原子操作,属于一种非常细粒度的锁,速度非常快。
Java中的AtomicInteger、AtomicBoolean、AtomicReference等大部分类都是采用了Unsafe
类的cas方法,这些方法都是native修饰,说明其是c++编写的,用来提供cpu指令级别的操作。
2、以AtomicBoolean为例
模拟多个生产者和多个消费者,但是数据只能存放一个,数据生成一个,消费一个,相当于一个传送带,生产者生产好数据临时放到传送带,如果消费者还没消费,生产者就必须等待。消费者同理。
使用AtomicBoolean.getAndSet()
方法提供原子操作,保证多个线程并发下程序的正确性。
public class AtomicBooleanExample {
private static AtomicBoolean isEmpty = new AtomicBoolean(true);
private static Random random = new Random();
private static String data;
private static int seq = 0;
public static void main(String[] args) {
String dataContentPrefix = "content-";
Runnable produceRunable = () -> {
while (true) {
if (isEmpty.getAndSet(false)) {
data = dataContentPrefix + ++seq;
System.out.println(String.format("%s put data %s", Thread.currentThread().getName(), data));
}
sleep(random.nextInt(1000));
}
};
Runnable consumeRunable = () -> {
while (true) {
if (!isEmpty.getAndSet(true)) {
System.out.println(String.format("%s get data %s", Thread.currentThread().getName(), data));
}
sleep(1000);
}
};
Thread producer1 = new Thread(produceRunable, "producer1");
Thread producer2 = new Thread(produceRunable, "producer2");
Thread consumer1 = new Thread(consumeRunable, "consumer1");
Thread consumer2 = new Thread(consumeRunable, "consumer2");
producer1.start();
producer2.start();
consumer1.start();
consumer2.start();
}
private static void sleep(long seconds) {
try {
TimeUnit.MILLISECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3、如果不使用AtomicBoolean,我们要如何保证isEmpty
变量的正确性
我们是不是得把变量设置为volatile保证可见性,同时为了保证原子性,还必须在设置isEmpty变量值时对其进行加锁!
public class NoAtomicBooleanExample {
private static volatile boolean isEmpty = true;
private static Object lock = new Object();
private static Random random = new Random();
private static String data;
private static int seq = 0;
public static void main(String[] args) {
String dataContentPrefix = "content-";
Runnable produceRunable = () -> {
while (true) {
synchronized (lock) {
if (isEmpty) {
data = dataContentPrefix + ++seq;
System.out.println(String.format("%s put data %s", Thread.currentThread().getName(), data));
isEmpty = false;
}
}
sleep(random.nextInt(1000));
}
};
Runnable consumeRunable = () -> {
while (true) {
synchronized (lock) {
if (!isEmpty) {
System.out.println(String.format("%s get data %s", Thread.currentThread().getName(), data));
isEmpty = true;
}
}
sleep(1000);
}
};
Thread producer1 = new Thread(produceRunable, "producer1");
Thread producer2 = new Thread(produceRunable, "producer2");
Thread consumer1 = new Thread(consumeRunable, "consumer1");
Thread consumer2 = new Thread(consumeRunable, "consumer2");
producer1.start();
producer2.start();
consumer1.start();
consumer2.start();
}
private static void sleep(long seconds) {
try {
TimeUnit.MILLISECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}