volatile,不保证原子性,只保证可见性,也就说不能保证线程安全。
如果是一写多读,那么jvm可以解决同步问题。但是,如果是多写,则线程并非安全。
实现原理是禁止指令重排序,并强制线程从主存储器获取数据。具体实现原理是在该数据发生更改时,线程会立刻将其写回主存。其他cpu都在嗅探主存储器写总线,当发现自己持有的缓存被修改时,对自己的缓存做无效处理,在下次访问时直接从主存储器中读取。
CAS操作
CAS有三个关键字,内存值V,旧的预期值A,和要修改的新值B,只有A=V时,则将V修改为B,否则什么都不做。
CAS通过调用JNI代码实现。CAS是x86,x64 CPU提供的元语(ARM最新版也添加了支持,以前都是用其他元语模拟的)。在最老的处理器上,采用的是锁住内存总线的方式,直接阻止其他cpu访问内存。
CAS操作会有ABA问题。
线程1准备用CAS将变量的值由A替换为B,在此之前,线程2将变量的值由A替换为C,又由C替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了,尽管CAS成功,但可能存在潜藏的问题。各种乐观锁的实现中通常都会用版本戳version来对记录或对象标记,避免并发操作带来的问题,在Java中,AtomicStampedReference
也实现了这个作用,它通过包装[E,Integer]的元组来对对象标记版本戳stamp,从而避免ABA问题,例如下面的代码分别用AtomicInteger和AtomicStampedReference来对初始值为100的原子整型变量进行更新,AtomicInteger会成功执行CAS操作,而加上版本戳的AtomicStampedReference对于ABA问题会执行CAS失败
详情请参考这篇博文
https://www.cnblogs.com/java20130722/p/3206742.html
使用java.util.concurrent.atomic可以在不用synchronized和lock情况下对变量进行原子性操作。
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class SimpleTest implements Runnable {
// 创建AtomicInteger,初始值0(也可以指定初始值)
// private static final AtomicInteger nextSerialNum = new AtomicInteger();
private static final AtomicLong nextSerialNum = new AtomicLong();
@Override
public void run() {
// 直接取得当前值并增长1
System.out
.println(Thread.currentThread().getName() + ":" + nextSerialNum.getAndIncrement());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
public static void main(String[] args) {
SimpleTest st = new SimpleTest();
for (int i = 0; i < 100; i++) {
new Thread(st, "Thread" + i).start();
}
}
}
一个典型的实际使用如下
Future<Boolean> xxxFuture = (Future<Boolean>) queryExecutor.submit(new TracerCallable() {
@Override
public Boolean doCall() throws Exception {
return xxxMaterialsSupportClient.isMayiCheMaterialExisted(certNo, userName, Materials.XXX_AVAILABLE_INFO.getDays());
}
});
Boolean isXXX = xxxFuture.get(requiredMaterialsResource.getTimeout(Materials.XXX_AVAILABLE_INFO.getCode()),
TimeUnit.MILLISECONDS);
if (isMayicheExisted) {
materials.add(Materials.XXX_AVAILABLE_INFO.getCode());
}
juc中的集合包含CopyOnWriteArrayList(实现list接口)和CopyOnWriteArraySet(实现set接口)。前者相当于线程安全的Arraylist,后者采用前者实现,相当于线程安全的HashSet。ConcurrentSkipListSet实现了set接口,相当于一个有序的treeset
ConcurrentHashMap,ConcurrentSkipListMap都实现了map接口。ConcurrentHashMap相当于线程安全的hashmap,而ConcurrentSkipListMap则是一个有序的哈希表,相当于一个线程安全的treemap。
实现queue接口的有以下几个类
##JUC中的lock
Lock接口
使用示例如下
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SellTicket implements Runnable {
// 定义票
private int tickets = 100;
// 定义锁对象
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
// 加锁
lock.lock();
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票");
}
} finally {
// 释放锁
lock.unlock();
}
}
}
}
ReadWriteLock,
定义了一些读者可以共享而写入着可以独占的锁。只有一个实现,就是ReentrantReadWriteLock。
##AQS(AbstractQueuedSynchronizer,队列同步器)
ReentrantLock,ReentrantReadWriteLock,CountDownLatch,CyclicBarrier和 Semaphore等这些类都是继承AQS类实现的。独占锁和共享锁的父类都是这个。这个类使用了模版方法,在这个类中只定义算法的骨架,而将一些步骤的实现延迟到子类中。
acquire方法会调用子类实现的tryAcquire尝试获取同步状态,如果失败,将线程作为node节点添加到同步队列(CLH)的队尾。这个队列的头部节点就是目前正在持有锁的节点。头部节点的对应线程在释放锁时,负责唤醒下一个节点。依赖LockSupport实现。
JUC的基础,底层依赖unsafe实现。
java可以通过继承Thread类或者实现Runnable接口来实现多线程,但是缺点是无法获得线程执行的结果。JUC为我们提供了这个FUTURETASK。类似runable接口中的实现run方法,我们可以实现callable接口中的call方法。这样,就可以让我们在线程结束后通过Future类实例获取线程的运行结果。
public class CallableAndFuture {
public static void main(String[] args) {
Callable<Integer> callable = new Callable<Integer>() {
public Integer call() throws Exception {
return new Random().nextInt(100);
}
};
FutureTask<Integer> future = new FutureTask<Integer>(callable);
new Thread(future).start();
try {
Thread.sleep(5000);// 可能做一些事情
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
参考:
https://www.cnblogs.com/nayitian/p/3264028.html