引子:关于Java并发中的volatile关键字
并切--定义
悲观锁:
1、一个线程在执行一个操作时持有对一个资源的独占锁(A线程占了资源a,则其他线程就不能操作资源a)
2、一般用在冲突比较可能发生的场景下
乐观锁:
1、尝试采用原子操作,而不需要持有锁;冲突可被检测,如果发生冲突,具有相应的重试逻辑。(不持有锁)
2、通常用在冲突比较少发生的场景下
非阻塞算法
1. 算法确保对线程间竞争共享资源时,不会因为互斥而使任一线程的执行无限延迟
无锁算法
1、如果系统整个流程的执行是无阻塞的(系统某一部分可能被短暂阻塞),这种非阻塞算法就是无锁的。
2、无锁算法比传统的基于锁算法对系统开销更小,且更容易在多核多CPU上扩展;
3、在实时系统中可以避免锁带来的延迟
4、CAS或LL/SC,以及内存屏障相关的指令经常被用在算法实现中
无等待算法
1、如果每个线程的执行都是无阻塞的,这种非阻塞算法就是无等待的(比无锁算法更好)
关于JAVA里面的并发
1、JAVA的内存模型并不保证一个线程可以一直以程序执行的顺序看到另一个线程对变量的修改,除非两个线程都跨越了同一个内存屏障。(safe publication)
Java内存模型
代码顺序规则
1、一个线程内的每个动作happens-before同一个线程内在代码顺序上在其后的所有动作
volatile变量规则
1. 对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入
我理解就是跨线程的变量读写,即A线程对变量的修改,B线程可以读到。?
传递性
1. 如果A happens-before B,B happens-before C,那A happens-before C
public class VolatileExample {
int x = 0;
volatile int b = 0;
private void write() {
x = 5;
b = 1;
}
private void read() {
int dummy = b;
while (x != 5) {
}
}
public static void main(String[] args) throws Exception {
final VolatileExample example = new VolatileExample();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
example.write();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
example.read();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
1、根据代码顺序规则,线程1的x=5; happens-before b=1;; 线程2的int dummy = b; happens-before while(x!=5);2、根据volatile变量规则,线程2的b=1; happens-before int dummy=b;
3、根据传递性,x=5; happens-before while(x!=5);
二)介绍下AtomicBoolean,AtomicInteger等这些Atomic*的类
atomic class可以帮助我们来简化同步处理。它的基本工作原理:使用了同步synchronized的方法来实现对一个long,integer,对象的增、减、赋值(更新)操作。比如对++运算操作,AtomicInteger可以将它持有的Integer能够atomic地递增。在需要访问两个或两个以上atomic变量的程序代码,通常需要被synchronize以便两者操作能够被当作是一个atomic的单元。
简单理解就是:对一个atomic类型的变量,多线程环境下对它进行增加或减少操作,不会出现线程问题. 这些比较原生的数据类型是线程安全的,而且支持无阻塞无锁定。
为了提升性能,atomic*并没有使用synchronized关键字,而是直接使用了最底层(本地C语言实现代码)来完成。
来看看在多线程环境下如果对同一个变量有同时操作的时候,atomic*的变量与非这种类型的变量会有什么处理上的差异?
三)关于CountDownLatch这个关键字
这是个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完成再执行。
常用的场景是:主线程去等待其子线程们都完成了各自的任务之后再继续往下走。比如主线程是一个主的工作流,子线程里面是各个执行器任务执行线程,主线程得等到子线程都完成了各自的任务之后才能往下执行。
实现原理:它是通过一个计数器来实现的,计数器初始值是线程的数量,当一个线程完成了自己的任务后,计数器的值就会减1.当计数器值到达0,它表示全部的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。
伪代码:
1. 主线程开启
2. 创建CountDownLatch
3. 开启N个子线程
4. 主线程在等
5. N个子线程都结束掉了
6. 主线程恢复继续
它的构造函数里面的参数就是要等待的线程数量,这个值只能设置一次,而且不能重设。
主线程必须要在启动其他线程之后立即调用.await()方法。这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自任务。
子线程一旦完成要通知countDownLatch对象,通知的方法就是调用CountDownLatch.countDown方法来完成,每调一次count就会减少1,所以当这个N个线程都调了count就会变0.(如果子线程抛异常没调到,就会一直阻塞主线程。)
最常见的场景:
1、实现最大的并行性:
如果想同时启N个线程,实现最大程度的并行性。
2、开始执行前等待N个线程先完成各自任务
比如在提供服务之前要确保N个服务都OK了再提供服务
3、死锁检测
可以使用N个线程访问共享资源
测试的一个示例:(看看里面的设计模式,抽象类的使用这块值得借鉴)
import java.util.concurrent.CountDownLatch;
/**
*
*/
public abstract class BaseHealthChecker implements Runnable {
private CountDownLatch _latch;
private String _serviceName;
private boolean _serviceUp;
public BaseHealthChecker(String serviceName, CountDownLatch latch) {
super();
this._latch = latch;
this._serviceName = serviceName;
this._serviceUp = false;
}
@Override
public void run() {
try {
verifyService();
_serviceUp = true;
} catch (Throwable t) {
t.printStackTrace();
_serviceUp = false;
} finally {
if (_latch != null) {
_latch.countDown();
}
}
}
public String getServiceName() {
return _serviceName;
}
public boolean isServiceUp() {
return _serviceUp;
}
public abstract void verifyService();
}
抽象的一个父类,有一个共同的方法叫run(). 然后各个子健康检查方式复用这个框架。
public class NetworkHealthCheck extends BaseHealthChecker {
public NetworkHealthCheck(CountDownLatch latch) {
super("Network Service", latch);
}
@Override
public void verifyService() {
System.out.println("开始检查网络是否OK" + this.getServiceName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getServiceName() + " is UP");
}
}
框架的意义:把公用的部分放到抽象父类里面去。子类写自己不一样的抽象方法。(比如我们可以写一个通用的执行器父类)
再加一个执行器
public class CacheHealthCheck extends BaseHealthChecker {
public CacheHealthCheck(CountDownLatch latch) {
super("Cache Service", latch);
}
@Override
public void verifyService() {
System.out.println("开始检查缓存是否OK" + this.getServiceName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getServiceName() + " is UP");
}
}
执行代码
public class ApplicationStartupUtil {
private static List<BaseHealthChecker> _services;
private static CountDownLatch _latch;
private ApplicationStartupUtil() {}
private final static ApplicationStartupUtil INSTANCE = new ApplicationStartupUtil();
public static ApplicationStartupUtil getInstance() {
return INSTANCE;
}
public static boolean checkExternalServices() throws InterruptedException {
_latch = new CountDownLatch(2);
_services = new ArrayList<BaseHealthChecker>();
_services.add(new NetworkHealthCheck(_latch));
_services.add(new CacheHealthCheck(_latch));
Executor executor = Executors.newFixedThreadPool(_services.size());
for (final BaseHealthChecker v : _services) {
executor.execute(v);
}
_latch.await();
for (final BaseHealthChecker v : _services) {
if (!v.isServiceUp()) {
return false;
}
}
return true;
}
public static void main(String[] args) throws InterruptedException {
boolean result = ApplicationStartupUtil.checkExternalServices();
System.out.println(result);
}
}