多线程(juc随笔)1

据说在java 1.5之前,如果涉及到多线程的场景,大家只能使用从1.0开始就存在的一个接口: Runnable,
或者说使用实现了Runnabel接口的Thread类


在1.5版本时,Doug Lea 在版本中编写了java.util.concurrent ,让线程的使用变得更加简单
笔者使用的jdk版本为1.9,但是考虑到1.9使用的人不多,在这里还是讨论1.8版本的内容。
在这个版本的java.util.concurrent包内,大致分了一下类:

juc.png

atomic

atomic包内包含有一系列支持原子操作的类,最常用的一般为AtomicBooleanAtomicIntegerAtomicLong
在这些类中,涉及到值的操作,都是调用的Unsafe中对应方法(或者是VarHandle中对应方法,参见Unsafe -> VarHandle

以AtomicInteger为例,这里的addAndGet方法实际调用的是VarHandle#getAndAdd

/**
     * Atomically adds the given value to the current value,
     * with memory effects as specified by {@link VarHandle#getAndAdd}.
     *
     * @param delta the value to add
     * @return the updated value
     */
    public final int addAndGet(int delta) {
        return U.getAndAddInt(this, VALUE, delta) + delta;
    }

locks

locks包内包含锁相关的类和接口,比如最核心的lock接口,其主要方法有lock()tryLock()unlock(),这里不做过多介绍

Lock接口

locks包内包含 LockCondition 相关内容

其中Lock接口

* {@code Lock} implementations provide more extensive locking
 * operations than can be obtained using {@code synchronized} methods
 * and statements.  They allow more flexible structuring, may have
 * quite different properties, and may support multiple associated
 * {@link Condition} objects.

简单来说,Lock接口实际上是对 synchronized的一个扩展,并提供了更加灵活的操作,比如其tryLock()方法,并且支持Condition 对象,关于Condition,我们稍后会进行讨论

Lock接口的基本方法有:

void lock();
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();

ReadWriteLock接口

在实际的场景中,大部分的情况下我们是读取数据,并不是修改或者写入数据,而传统的synchronized关键字并不支持对读写场景的区分,在jdk1.5之后也加入了ReadWriteLock接口,提供读写锁的支持

每个读写锁由读锁和写锁构成:

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading
     */
    Lock readLock();

    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing
     */
    Lock writeLock();
}

必须注意的是,所有实现ReadWriteLock实现必须保证,成功获取readLock的线程将看到在先前释放writeLock所做的所有更新

以下内容来自维基百科:

读写锁是计算机程序的并发控制的一种同步机制,也称“共享-互斥锁”、多读者-单写者锁。[[1]](https://zh.wikipedia.org/wiki/%E8%AF%BB%E5%86%99%E9%94%81#cite_note-Hamilton-1)多读者锁,[2],“push lock”[3]) 用于解决读写问题。读操作可并发重入,写操作是互斥的。

线程池相关

Executor

一个Executor应该能执行被提交的Runnable方法,并且这个接口将任务提交和任务执行之间解耦了

实际上,许多Executor的实现类会对任务执行进行调度,并将任务序列化后交给第二个executor,如下所示

import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.Executor;

public class SerialExecutor implements Executor {
    final Queue tasks = new ArrayDeque();
    final Executor executor;
    Runnable active;

    SerialExecutor(Executor executor) {
        this.executor = executor;
    }
    @Override
    public synchronized void execute(final Runnable r) {
        tasks.offer(new Runnable() {
            @Override
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (active == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((active = tasks.poll()) != null) {
            executor.execute(active);
        }
    }
}

Executors

总的来说这个类是线程池的工厂类,提供了多种线程池的生成静态方法


ExecutorService

  • ExecutorService 继承了Executor接口,并且提供了 shutdown()方法来关闭线程池
  • Executor只能接受Runnable任务,而ExecutorService还能接受Callable任务
  • Executorsubmit()方法没有返回值,ExecutorService中提供了Future型的返回值

Future

Future代表一个异步计算的结果,并且提供了对应的方法来判断任务是完成还是取消了,也提供了等待任务完成的方法。

 * interface ArchiveSearcher { String search(String target); }
 * class App {
 *   ExecutorService executor = ...
 *   ArchiveSearcher searcher = ...
 *   void showSearch(final String target)
 *       throws InterruptedException {
 *     Future future
 *       = executor.submit(new Callable() {
 *         public String call() {
 *             return searcher.search(target);
 *         }});
 *     displayOtherThings(); // do other things while searching
 *     try {
 *       displayText(future.get()); // use future
 *     } catch (ExecutionException ex) { cleanup(); return; }
 *   }
 * }}

你可能感兴趣的:(多线程(juc随笔)1)