Java随谈(四)JDK对并发的支持

Java对并发的支持

在Java诞生之时,Java设计者们就考虑了并发的问题,但受限于当时的技术和需求,只是对其进行了简单的支持。
随着时代更替(多核处理器的普及,提高了程序员对处理器的使用效率的诉求),并发成为了一个需要关注的功能点。

这里,博主将概述Java各个版本(截止到JDK8)对于并发的支持的发展,便于理解这些类、接口,为何出现,又为了解决哪些问题。

历代版本

在JDK1.4及之前

  1. 1.synchronized、volatile关键字
  2. Object的wait()、notify()、notifyAll()方法
  3. Thread类,以及Runnable接口
  4. final 不变属性以及一些不变的类,比如String
  5. 基础但已经不被推荐使用的集合 Vector、HashTable

在这个时期,Java对并发做的支持比较底层,需要程序员手动管理需要同步的类或对象。这种情况下,除非程序员对并发有非常深的理解,才能用好这些内容。(过于底层,不易理解,编程困难
而且由于synchronized是重量级锁,需要经常切换线程,效率比较低。(并发效率不高

JDK1.5

  1. AtomicXXX类
  2. Lock显式锁(Lock、ReadWriteLock、Condition等接口)
  3. Callable、Future接口
  4. Executor执行器、ExecutorService两个接口以及实现类
  5. 并发集合BlockingQueue、ConcurrentMap、ConcurrentHashMap、CopyOnWriteArrayList

在这个时期,Java对并发进行了大刀阔斧的改进。
新增的原子操作类型比volatile关键字更易用且高效;(简化并发编程,优化性能)
Lock显示锁不同于synchronized引入了一种新的机制;(引入一种轻量的锁)
执行器使用了线程池机制,简化并发操作。(简化并发编程)
新增的并发集合比起之前的Vector、HashTable(仅在方法上添加synchronized关键词),引入了更多机制,性能也更高。

JDK1.6

  1. 优化后的synchronized关键字,使用分级锁机制。

这个时期,对同步关键字进行了优化、之前只存在一种重量级锁,后引入了偏向锁轻量级锁,减少了线程切换的次数。(从语法上优化了并发性能)
优化后的synchronized关键字性能可以和Lock媲美。

JDK7

  1. Fork/Join框架(分解/合并框架)
  2. TransferQueue接口以及实现类
  3. ThreadLocalRandom 支持并发随机数
  4. Phaser工具类 阶段性并发工具类

在这个时期,提供了使用分治技术的Fork/Join框架等。

JDK8

  1. XXXAdder加法器、XXXAccumulator累加器(优化后的原子类型操作)
  2. CompletableFuture(Future升级版本)
  3. StampedLock (ReadWriteLock改进版本)
  4. 静态commonPool(为ForkJoinPool提供了多一种选择)
  5. StampedLock(新增在写,读和乐观读的三种模式转化的锁)

线程运用的代码示例

JDK 1.4 synchronized + 线程类


public class Counter {
    /**
     * 计数器
     */
    private int count = 0;

    /**
     * 计数器加1
     */
    public synchronized void increment() {
        count = count + 1;
    }

    /**
     * 获取计数器的值
     * 
     * @return 计数器的值
     */
    public synchronized int getCount() {
        return count;
    }

    // 多线程操作(手动创建线程)
    public static void main(String[] args) throws Exception {
        Counter counter = new Counter();
        // 创建两个线程,分别对计数器进行1000次加1操作
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });
        // 启动两个线程
        t1.start();
        t2.start();
        // 等待两个线程执行完毕
        t1.join();
        t2.join();
        // 输出线程的执行结果 2000
        System.out.println(counter.getCount());
    }
}

‌JDK 5:并发工具类(Atomic class 原子类) + 线程池

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class Counter {
    /**
     * 使用AtomicInteger类创建计数器,初始化为0
     */
    private AtomicInteger count = new AtomicInteger(0);

    /**
     * 计数器自增, 并返回当前值
     */
    public int increment() {
        return count.incrementAndGet();
    }

    /**
     * 获取当前计数器的值
     *
     * @return 当前计数器的值
     */
    public int getCount() {
        return count.get();
    }

    public static void main(String[] args) throws Exception {
        // 创建一个线程数量为2的线程池
        ExecutorService executor = Executors.newFixedThreadPool(2);
        Counter counter = new Counter();

        // 提交两个任务给线程池,每个任务都对计数器进行1000次自增操作
        Future<?> f1 = executor.submit(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });
        Future<?> f2 = executor.submit(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        // 等待两个任务执行完成
        f1.get();
        f2.get();
        // 关闭线程池
        executor.shutdown();
        // 打印计数器的值 2000
        System.out.println(counter.getCount());
    }
}

‌JDK 8:CompletableFuture‌、并行流

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.IntStream;

// 使用线程池和Atomic类
public class Counter {
    public static void main(String[] args) {
        // 例子1 使用CompletableFuture,异步编程,多用于网络请求等
        // 创建高并发计数器
        LongAdder adder = new LongAdder();
        // 并行执行两个任务, 每个任务执行1000次自增操作
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            IntStream.range(0, 1000).forEach(i -> adder.increment());
        });
        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
            IntStream.range(0, 1000).forEach(i -> adder.increment());
        });
        // 等待两个任务执行完成
        CompletableFuture.allOf(future1, future2).join();
        // 输出结果 2000
        System.out.println(adder);

        // 例子2 使用并行流,批量并行计算,多用于数值计算等
        // 创建高并发累加器
        LongAccumulator accumulator = new LongAccumulator(
            Long::sum,
            0L
        );
        // 并行执行两个任务, 每个任务执行1000次自增操作
        IntStream.range(0, 1000).parallel().forEach(i -> accumulator.accumulate(1));
        IntStream.range(0, 1000).parallel().forEach(i -> accumulator.accumulate(1));
        // 输出结果 2000
        System.out.println(accumulator.get());
    }
}
posted @ 2020-11-18 19:29  Kwanwooo  阅读(231)  评论(0)    收藏  举报