../

Java并发总结(一)

本文只是对Java并发知识的一个总结, Java并发是一个很难的问题,不是一篇博文能够说清,以后还需在实践中多多积累学习才是。

多线程的概念

多线程程序在较低的层次上扩展了多任务的概念:一个程序同时执行多个任务。通常, 每一个任务称为一个线程( thread), 它是线程控制的简称。可以同时运行一个以上线程的程 序称为多线程程序(multithreaded)。 那么,多进程与多线程有哪些区别呢? 本质的区别在于每个进程拥有自己的一整套变 量, 而线程则共享数据。 这听起来似乎有些风险, 的确也是这样, 在本章稍后将可以看到这 个问题。然而,共享变量使线程之间的通信比进程之间的通信更有效、 更容易。 此外, 在有 些操作系统中,与进程相比较, 线程更“ 轻量级”, 创建、 撤销一个线程比启动新进程的开 销要小得多。

线程状态

Java线程包含如下状态:

线程属性

Java线程包含如下属性:

优先级

public final static int MIN_PRIORITY = 1;

public final static int NORM_PRIORITY = 5;

public final static int MAX_PRIORITY = 10;

public final void setPriority(int newPriority) {
    ThreadGroup g;
    checkAccess();
    if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
        throw new IllegalArgumentException();
    }
    if ((g = getThreadGroup()) != null) {
        if (newPriority > g.getMaxPriority()) {
            newPriority = g.getMaxPriority();
        }
        setPriority0(priority = newPriority);
    }
}

Java线程优先级默认值为5,取值区间为1-10的一个整数

守护线程

public final void setDaemon(boolean on) {
    checkAccess();
    if (isAlive()) {
        throw new IllegalThreadStateException();
    }
    daemon = on;
}
public static void main(String[] args) {
    Thread thread = new Thread(new MyRunnable());
    thread.setDaemon(true);
}

守护线程是程序运行时在后台提供服务的线程,不属于程序中不可或缺的部分。

当所有非守护线程结束时,程序也就终止,同时会杀死所有守护线程。

main() 属于非守护线程。

在线程启动之前使用 setDaemon() 方法可以将一个线程设置为守护线程。

线程创建方式

有三种使用线程的方式:

线程池

Executor的类型

Executor 管理多个异步任务的执行,而无需程序员显式地管理线程的生命周期。这里的异步是指多个任务的执行互不干扰,不需要进行同步操作。 主要有三种Executor:

Executor的中断操作

调用 Executor 的 shutdown() 方法会等待线程都执行完毕之后再关闭,但是如果调用的是 shutdownNow() 方法,则相当于调用每个线程的 interrupt() 方法

private static final int CORE_POOL_SIZE = 5;
private static final int MAX_POOL_SIZE = 10;
private static final int QUEUE_CAPACITY = 100;
private static final Long KEEP_ALIVE_TIME = 1L;

public static void main(String[] args) {

    //使用阿里巴巴推荐的创建线程池的方式
    //通过ThreadPoolExecutor构造函数自定义参数创建
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAX_POOL_SIZE,
            KEEP_ALIVE_TIME,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(QUEUE_CAPACITY),
            new ThreadPoolExecutor.CallerRunsPolicy());
}

如果只想中断 Executor 中的一个线程,可以通过使用 submit() 方法来提交一个线程,它会返回一个 Future<?> 对象,通过调用该对象的 cancel(true) 方法就可以中断线程

Future<?> future = executorService.submit(() -> {
    // ..
});
future.cancel(true);

同步

线程之间是有共享内存的,为了防止运行时出现数据竞争,Java提供了一系列的工具来解决这个问题

Synchronized

内置锁,当它用来修饰一个方法或者代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码,另一个线程必须等待当前线程执行完以后才能执行该段代码(未执行之前,该线程处于阻塞状态)。同时它也是一个可重入锁。

  1. 同步一个类
public static Singleton getInstance() {
    if (singleton == null) {
        synchronized (Singleton.class) {
            if (singleton == null) {
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}

上述代码作用于整个类,也就是说两个线程调用同一个类的不同对象上的这种同步语句,也会进行同步。(如果锁住this,那么就只有同一个对象的同步语句才会进行同步)

  1. 同步一个方法
public synchronized void func () {
    // ...
}

作用于同一个对象。

ReentrantLock

ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁

public class LockExample {

    private Lock lock = new ReentrantLock();

    public void func() {
        lock.lock();
        try {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + " ");
            }
        } finally {
            // 确保释放锁,从而避免发生死锁
            lock.unlock();
        }
    }
}

比较

  1. 锁的实现 synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。
  2. 性能 新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等,synchronized 与 ReentrantLock 大致相同。
  3. 等待可中断 当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。ReentrantLock 可中断,而 synchronized 不行。
  4. 公平锁 公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。 公平锁将由于在挂起线程和恢复线程时存在的开销而极大的降低效率。 而非公平锁由于是在请求时锁已经为可用状态就直接获取,不需要进行什么额外的操作,因此效率更高。 synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。
  5. 锁绑定多个条件 一个 ReentrantLock 可以同时绑定多个 Condition 对象。

线程之间的协作

当多个线程可以一起工作去解决某个问题时,如果某些部分必须在其它部分之前完成,那么就需要对线程进行协调。

join()

在线程中调用另一个线程的 join() 方法,会将当前线程挂起,而不是忙等待,直到目标线程结束。

wait() / notify() / notifyAll()

wait() 和 sleep() 的区别

await() / signal() / signalAll()

小结

这次总结了线程的基本概念,以及锁的运用,下次介绍下Java并发编程的工具箱😄

参考