JUC入门

121 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第17天,点击查看活动详情

JUC简介

在 Java 5.0 提供了 java.util.concurrent (简称 JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步IO和轻量级任务框架。

JUC是用于解决多线程同步问题,给java开发者提供便利的工具。

进程和线程的区别

进程是指在系统中正在运行的一个应用程序。进程是执行程序的实例。

线程是系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元。 对于操作系统而言,其调度单元是线程。

一个进程至少包括一个线程,通常将该线程称为主线程。一个进程从主线程的执行开始进而创建一个或多个附加线程,就是所谓基于多线程的多任务。

volatile关键字

volatile是JAVA虚拟机提供轻量级同步机制。

volatile特性

  1. 保证可见性。
  2. 不保证原子性。
  3. 禁止指令重排。

其中可见性是其重要特性。那么什么是可见性?

可见:某个线程对 被volatile修饰的对象,可被其他线程可见。

所谓原子性操作就是指这些操作是不可中断的,要么一定做完,要么就没有执行。

简单测试一下volatile的不保证原子性

用一个原子操作,一个用volatile来操作,作对比看看有什么区别

/**
 * @author miracle
 */
public class Num {
    /**
     * volatile 可见性,但不保证原子性
     */
    volatile int num = 0;

    /**
     * 保证原子性
     */
    AtomicInteger atomicNum = new AtomicInteger();

    void plus() {
        this.num += 1;
    }


    /**
     * 原子性操作
     */
    public void atomicPlus() {
        atomicNum.getAndIncrement();
    }


}

测试用例

public static void main(String[] args) {

    Num number = new Num();

    for(int i = 0; i < 20; i++) {

        new Thread(() -> {
            for(int j = 0; j < 1000; j++) {
                number.plus();
                number.atomicPlus();;
            }

        }).start();
    }

    // 默认有 main 线程和 gc 线程,等待20个线程处理完,再获取值
    while (Thread.activeCount() > 2) {
        Thread.yield();
    }


    System.out.println("num-->" + number.num);
    System.out.println("atomicNu-->" + number.atomicNum);
}

输出结果:

num-->19932

atomicNu-->20000

总结:在多线程下使用volatile不能保证同步。因为其不保证原子性。

创建多线程的方式

  1. 继承Thread类。
  2. 实现Runnable接口。
  3. 实现Callable接口。
  4. 线程池。

线程池代码示例: image.png

线程锁

1. Lock同步锁。

在Java 5.0之前,协调共享对象的访问时可以使用的机制只有synchronized和volatile。Java 5.0后增加了一些新的机制,但并不是一种替代内置锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能。

ReentrantLock实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。但相较于synchronized提供了更高的处理锁的灵活性。 image.png

2. synchronized锁。

synchronized和lock锁的区别

  1. synchronized内置的java关键字,Lock是一个接口。
  2. synchronized无法判断获取锁的状态, Lock可以判断是否获取到了锁。
  3. synchronized会自动释放锁,Lock必须要手动释放锁!如果不是释放锁,会产生死锁。
  4. synchronized可以作用在方法和代码块上,而lock只能作用在代码块上。
  5. synchronized是非公平锁,而lock可以是公平锁也可以是非公平锁。

线程八锁

线程调度