面试Java,必然要被问Java内存模型和Java并发开发。我被问到的时候,内心慌得一批,“额,是在《Thinking in Java》里面写的吗?果真天天增删改太low了”面试
Java 并发代码shell
复制代码
public class Example1 {
public static int count = 0;
public static int clientTotal = 5000;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
add();
} catch (Exception e) {
log.error("exception", e);
}
});
}
}
private static void add() {
count++;
}
}
若是上面代码执行,count的值是多少?(为了说明重点问题,没有写最后打印的代码) 5000?屡次运行的结果,count的值是小于5000的。bash
解释一下上面的程序,首先定义了一个线程池,启动5000个线程执行add()操做,add函数处理静态成员变量count。多线程
若是程序顺序调用,count的值应该是5000。并发
复制代码
for(int i=0;i<5000;i++){
add();
}
可是线程池启动多线程,是并发执行的。每一个线程启动以后,无论是否运行结束,下一个线程会立刻启动。异步
启动线程的过程,是一个异步过程,启动线程当即返回,启动下一个进程。函数
当多个线程对同一个变量add进行操做的时候,就会发生写写冲突。ui
线程一、线程2 同时对值为0的变量进行操做,结果返回1,而不是2。若是这个地方想不明白,就请留言,或者看看文章顶部那些原理图。spa
要不简单点,记住“多线程对全局变量的写操做会发生冲突”。线程
复制代码
public class CountExample2 {
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
public static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count.get());
}
private static void add() {
count.incrementAndGet();
// count.getAndIncrement();
}
}
注,上面的代码用了生成者消费者模式,5000个生产者,200个消费者,对程序并发作必定限制,防止5000个线程卡死计算机。
这个就是C的内存模型,作shellcode的基础知识。