在java5.0之后推出了java.util.concurrent(简称JUC)包,在此包中提供了很多在并发编程中使用的工具类,用于定义类似于
线程的自定义子系统,包括线程池,异步IO和轻量级任务框架,还提供了用于多线程上下文中的 Collection实现等。
并发编程中有三个特性:
原子性(互斥性) : 代码不可拆封,i++ 就不是原子操作,它可以拆分为三个步骤 temp1=i;
temp2=temp1+1;
i=temp2;
指令重排序:虚拟机在编译程序时,为了优化,可能不会按照你所写的代码循序执行
内存可见性:在多线程同时访问一个临界资源,当一个线程改变临界资源值的时候,其它线程要知道!
volatile 关键字: 当多个线程进行操作共享数据时,可以保证内存中的数据是可见的;相较于 synchronized 是一种
较为轻量级的同步策略;
volatile 不具备互斥性。
synchronized和volatile的区别
synchronized可以保证原子性,内存可见性,但不能保证指令重排序。
voliatle可以保证内存可见性,禁止指令重排序,但不能保证原子性
public class MyThread implements Runnable{
/* int n=0;
@Override
public void run() {
while (true){
synchronized (this) {
if(n>=10) {
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(n++);
}
}
}*/
//下面使用的是原子变量操作 和上面n++效果相同
private AtomicInteger ai = new AtomicInteger(0); /创建这个类对象 初始值为1
@Override
public void run() {
System.out.println(ai.getAndIncrement());//getAndIncrement()自增1
}
}
//测试类
public class MyThreadTest{
public static void main(String[] args) {
MyThread myThread = new MyThread();
for (int i=0;i<10;i++) {
new Thread(myThread).start();
}
}
}
是硬件对并发的支持,针对多处理器操作而设计的处理处理器中的一种操作指令,用于管理对共享数据的并发访问。
CAS是一种无锁的非阻塞算法(乐观锁)的实现。 阻塞(同一时刻,只允许一个线程访问临界资源,加锁)
CAS 包含了三个操作数:
进行比较的旧预估值: A
需要读写的内存值: V
将写入的更新值: B
当且仅当 A == V 时, V = B, 否则,将不做任何操作,并且这个比较交换过程属于原子操作;
//模拟CAS算法
public class CompareAndSwapDemo {
public static void main(String[] args) {
CompareAndSwap compareAndSwap = new CompareAndSwap();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int expect = compareAndSwap.get();
boolean b = compareAndSwap.compareAndSwap(expect, new Random().nextInt(101));
System.out.println(b);
}
}).start();
}
}
}
class CompareAndSwap {
private int value;
/**
* 获取值
*
* @return
*/
public synchronized int get() {
return value;
}
public synchronized boolean compareAndSwap(int expect, int newValue) {
if (this.value == expect) {
this.value = newValue;
return true;
}
return false;
}
}
在CAS算法中,需要取出内存中某时刻的数据(由用户完成),在下一时刻比较并替换(由CPU完成,该操作是原子的)。这个时间差中,会导致数据的变化。
假设如下事件序列:
线程 1 从内存位置V中取出A。
线程 2 从位置V中取出A。
线程 2 进行了一些操作,将B写入位置V。
线程 2 将A再次写入位置V。
线程 1 进行CAS操作,发现位置V中仍然是A,操作成功。
尽管线程 1 的CAS操作成功,但不代表这个过程没有问题——对于线程 1 ,线程 2 的修改已经丢失。
CAS算法会出现ABA问题,为了解决这个问题可以增加版本号,记录更改次数(AtomicStampedReference)
//使用AtomicStampedReference解决ABA问题
public static void main(String[] args) {
AtomicStampedReference asr = new AtomicStampedReference(1, 1);
for (int i = 0; i < 50; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Object old = asr.getReference(); //获取旧值
int oldStamp = asr.getStamp();//获取版本号
// 旧值 新值 老的标记 新标记
boolean b = asr.compareAndSet(old,new Random().nextInt(1000), oldStamp,oldStamp+1);
System.out.println(b);
}
}).start();
}
}