目录

hymn

忽有故人心头过,回首山河已是秋。

标签: 多线程 (20)

线程池状态 有更新!

ThreadPoolExecutor继承了AbstractExecutorService,成员变量ctl是一个Integer的原子变量,用来记录线程池状态和线程池中线程个数,类似于ReentrantReadWriteLock使用一个变量来保存两种信息。

java 并发队列总结 有更新!

ConcurrentLinkedQueue 无界非阻塞队列,底层用单链表实现,出队和入队用cas实现。 LinkedBlockingQueue 底层用单链表实现,有界,独占锁实现出队和入队,有两个锁,takeLock和putLock takeLock:控制同时只能有一个线程可以从队头获取元素,其他线程等待 putLock:控制同时只能有一个线程可以从队尾添加元素,其他线程等待 ArrayBlockingQueue 底层数组实现,通过锁实现出队和入队,有界(可以指定,默认Integer.Max_VALUE) PriorityBlockingQueue 带有优先级的无界队列,底层通过二叉树堆实现, DelayQueue 通过 PriorityBlockingQueue来装数据,只有过期的数据才会出队。

AQS 有更新!

AbstractQueuedSynchronizer抽象同步队列简称AQS,它是实现同步器的基础组件,并发包中锁的底层就是使用AQS实现的 AQS是一个FIFO的双向队列,其内部通过节点 head和 tail记录队首和队尾元素,队列元素的类型为 Node。 其中 Node中的 thread变量用来存放进入AQS队列里面的线程; Node节点内部的 SHARED用来标记该线程是获取共享资源时被阻塞挂起后放入AQS队列的, EXCLUSIVE用来标记线程是获取独占资源时被挂起后放入AQS队列的; waitStatus记录当前线程等待状态,可以为 CANCELLED(线程被取消了)、SIGNAL(线程需要被唤醒)、CONDITION(线程在条件队列里面等待)、PROPAGATE(释放共享资源时需要通知其他节点); prev记录当前节点的前驱节点,next记录当前节点的后继节点。 在AQS中维持了一个单一的状态信息 state,可以通过 getState、setState、compareAndSetState函数修改其值。 对于 ReentrantLock的实现来说,state可以用来表示....

ThreadLocal 有更新!

ThreadLocal 只是个工具类,真正的数据都是存在当前的线程内, get() public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; } 当线程调用 get() 时,是返回当前线程的 threadLocals,正真的数据就在这里。 set() public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap ma....

interrupt() interrupted() isInterruped() 区别

void interrupt() 中断线程,例如A线程运行,B线程可以调用A的interrupt()方法来设置线程A的中断标志为true boolean interrupted() 检测当前线程是否被中断,当前线程指的是:在那个线程中调用,不是那个实例的调用。比如在主线程中调用线程 A 的 interrupted ,获取的还是主线程的中断状态。 另外在调用之后会清除当前线程的中断状态,如果当前的中断状态是 true ,调用完后变为 false 。 boolean isInterruped() 检查当前线程是否被中断,这个当前线程指的是:那个线程实例调用的,比如在主线程中调用A的 isInterruped() ,获取的是A线程的中断状态。不清楚当前线程的中断状态。

使用ThreadLocal

在一个线程中,横跨若干方法调用,需要传递的对象,我们通常称之为上下文(Context),它是一种状态,可以是用户身份、任务信息等。 给每个方法增加一个context参数非常麻烦,而且有些时候,如果调用链有无法修改源码的第三方库,User对象就传不进去了。 Java标准库提供了一个特殊的 ThreadLocal,它可以在一个线程中传递同一个对象。 ThreadLocal实例通常总是以静态字段初始化如下: static ThreadLocal<User> threadLocalUser = new ThreadLocal<>(); 它的典型使用方式如下: void processUser(user) { try { threadLocalUser.set(user); step1(); step2(); } finally { threadLocalUser.remove(); } } 通过设置一个 User实例关联到 ThreadLocal中,在移除之前,所有方法都可以随时获取到该 User实例: void step1() { User u = threadL....

ForkJoin

Java 7开始引入了一种新的Fork/Join线程池,它可以执行一种特殊的任务:把一个大任务拆成多个小任务并行执行。 我们举个例子:如果要计算一个超大数组的和,最简单的做法是用一个循环在一个线程内完成: ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘ 还有一种方法,可以把数组拆成两部分,分别计算,最后加起来就是最终结果,这样可以用两个线程并行执行: ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘ ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘ 如果拆成两部分还是很大,我们还可以继续拆,用4个线程并行执行: ┌─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴─┘ ┌─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴─┘ ┌─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴─┘ ┌─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴....

使用Future 和 CompletableFuture 有更新!

Future ExecutorService executor = Executors.newFixedThreadPool(4); // 定义任务: Callable<String> task = new Task(); // 提交任务并获得Future: Future<String> future = executor.submit(task); // 从Future获取异步执行返回的结果: String result = future.get(); // 可能阻塞 当我们提交一个 Callable任务后,我们会同时获得一个 Future对象,然后,我们在主线程某个时刻调用 Future对象的 get()方法,就可以获得异步执行的结果。在调用 get()时,如果异步任务已经完成,我们就直接获得结果。如果异步任务还没有完成,那么 get()会阻塞,直到任务完成后才返回结果。 一个 Future<V>接口表示一个未来可能会返回的结果,它定义的方法有: get():获取结果(可能会等待) get(long timeout, TimeUnit uni....

ScheduledThreadPool 细节

创建一个 ScheduledThreadPool仍然是通过 Executors类: ScheduledExecutorService ses = Executors.newScheduledThreadPool(4); 我们可以提交一次性任务,它会在指定延迟后只执行一次: // 1秒后执行一次性任务: ses.schedule(new Task("one-time"), 1, TimeUnit.SECONDS); 如果任务以固定的每3秒执行,我们可以这样写: // 2秒后开始执行定时任务,每3秒执行: ses.scheduleAtFixedRate(new Task("fixed-rate"), 2, 3, TimeUnit.SECONDS); 如果任务以固定的3秒为间隔执行,我们可以这样写: // 2秒后开始执行定时任务,以3秒为间隔执行: ses.scheduleWithFixedDelay(new Task("fixed-delay"), 2, 3, TimeUnit.SECONDS); 注意FixedRate和FixedDelay的区别。FixedRate是指任务总是以....

java.util.concurrent 有更新!

ReentrantLock java.util.concurrent.locks包提供的 ReentrantLock用于替代 synchronized加锁 public class Counter { private int count; public void add(int n) { synchronized(this) { count += n; } } } public class Counter { private final Lock lock = new ReentrantLock(); private int count; public void add(int n) { lock.lock(); try { count += n; } finally { lock.unlock(); } } } // 可以尝试去获取锁,尝试获取锁的时候,最多等待1秒。 // 如果1秒后仍未获取到锁,tryLock()返回false, // 程序就可以做一些额外处理,而不是无限等待下去 if (lock.tryLock(1, TimeUnit.SECONDS)) { try { .......