大家好,我是哪吒。
上一篇简单聊一聊公平锁和非公平锁,parallel并行流,提到了一个IntStream.rangeClosed并行流问题,很多小伙伴,对这个比较陌生,想用线程池的方式改造一下。
@Data
public class LockTest1 {
public static void mAIn(String[] args) {
IntStream.rangeClosed(1, 100000).parallel().forEach(i -> new LockTest1().increase());
System.out.println(time);
}
private static int time = 0;
private static Object lock = new Object();
public void increase() {
synchronized (lock) {
time++;
}
}
}
不会那些新特性,还是原始的香啊,写起代码,得心应手。
这时候,有些小伙伴,又陷入了选择恐惧症。用哪个线程池比较好呢?
简单回顾一下:
最常用的还是固定线程数线程池newFixedThreadPool(10)。
@Data
public class LockTest2 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(200);
for (int i = 0; i < 100000; i++) {
Thread0926 thread = new Thread0926();
executorService.execute(thread);
}
System.out.println(time);
}
private static int time = 0;
private static Object lock = new Object();
public void increase() {
synchronized (lock) {
time++;
}
}
}
public class Thread0926 implements Runnable{
@Override
public void run() {
LockTest2 lockTest = new LockTest2();
lockTest.increase();
}
}
我草,这不对啊,不应该是100000嘛?又把老子整不会了~
上一篇测试过,使用synchronized代码块是可以解决i++线程安全问题的,这次怎么不好使了?
上面的代码中,synchronized (lock)锁住了time++,lock是静态变量,所以属于类级别的锁。但是新建的线程是一个新的类,超出了锁的范围,所以失效。
那么,在当前类中,开启线程,是不是就可以了呢?试一下
public class LockTest4 {
private static int time = 0;
private static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 200; i++) {
Thread thread = new Thread(() -> {
for (int j = 0; j < 500; j++) {
synchronized (lock) {
time++;
}
}
});
thread.start();
}
Thread.sleep(3000);
System.out.println(time);
}
}
当然,在synchronized代码块中,使用synchronized (LockTest4.class)也是可以的,效果是一样的。
并行流的本质的是并行,多线程的本质是并发。
并行指当多核CPU中的一个CPU执行一个线程时,其它CPU能够同时执行另一个线程,两个线程之间不会抢占CPU资源,可以同时运行。
并发指在一段时间内CPU处理多个线程,这些线程会抢占CPU资源,CPU资源根据时间片周期在多个线程之间来回切换,多个线程在一段时间内同时运行,而在同一时刻不是同时运行的。
场景应用程序开发,提供http接口、数据库查询、微服务调用都是IO请求,IO请求时几乎不消耗cpu,这是为了提供cup使用率,建议使用多线程并发,线程数可以远大于cpu核数。
对应大量的加减乘除运算、md5、hash等运算操作,需要持续使用cpu,需要让多核cpu并行运算,适合使用forkjoin并行计算。