目录
6、Callable、Future和FutureTask用法与线程池的连用:
6.1、Future和Callable与线程池获取执行结果:
6.3、Callable和Future,线程池提交闭包操作:
10、非阻塞队列ConcurrentLinkedQueue:
1、Thread和Runnable:
1.1、继承Thread:
package com.busymonkey.concurrent;
public class ThreadTest {
public static void main(String[] args) {
ThreadFun1 thread = new ThreadFun1();
thread.start();
}
}
class ThreadFun1 extends Thread {
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello!");
}
}
}
1.2、实现Runnable接口:
package com.busymonkey.concurrent;
public class RunnableTest {
public static void main(String[] args) {
ThreadFun2 thread = new ThreadFun2();
Thread t = new Thread(thread);
t.start();
}
}
class ThreadFun2 implements Runnable {
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello!");
}
}
}
1.3、Thread和Runnable闭包创建线程:
package com.busymonkey.concurrent;
public class ThreadTestClosure {
public static void main(String[] args) {
for (int i = 1; i < 5; i ++) {
final int taskId = i;
new Thread(new Runnable() {
public void run() {
for (int i = 1; i < 5; i ++) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task : " + taskId + "; run time :" + i);
}
}
}).start();
}
}
}
package com.busymonkey.concurrent;
public class ThreadTestClosure {
public static void main(String[] args) {
for (int i = 1; i < 5; i ++) {
final int taskId = i;
new Thread() {
public void run() {
for (int i = 1; i < 5; i ++) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task : " + taskId + "; run time :" + i);
}
}
}.start();
}
}
}
2、ExecutorService :
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这 样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
Executors 返回的线程池对象的弊端如下:
FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
CachedThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
2.1、瞬时线程:
(开辟固定数量、动态开辟、开辟单个线程)测试发现开辟固定数量1个线程和开辟单个线程方法是一样效果:
package com.busymonkey.concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest {
public static void main(String[] args) {
// 创建可以容纳3个线程的线程池
//ExecutorService threadPool = Executors.newFixedThreadPool(1);
// 线程池的大小会根据执行的任务数动态分配
//ExecutorService threadPool = Executors.newCachedThreadPool();
// 创建单个线程的线程池,如果当前线程在执行任务时突然中断,则会创建一个新的线程替代它继续执行任务
ExecutorService threadPool = Executors.newSingleThreadExecutor();
for (int i = 1; i < 5; i++) {
final int taskID = i;
threadPool.execute(new Runnable() {
public void run() {
for (int i = 1; i < 5; i++) {
try {
Thread.sleep(20);// 为了测试出效果,让每次任务执行都需要一定时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第" + taskID + "次任务的第" + i + "次执行");
}
}
});
}
threadPool.shutdown();// 任务执行完毕,关闭线程池
}
}
2.2、定时线程和周期线程:
package com.busymonkey.concurrent;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolTestTimer {
public static void main(String[] args) {
ScheduledExecutorService schedulePool = Executors.newScheduledThreadPool(1);
// 5秒后执行任务
schedulePool.schedule(new Runnable() {
public void run() {
System.out.println("爆炸");
}
}, 5, TimeUnit.SECONDS);
// 5秒后执行任务,以后每2秒执行一次
schedulePool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("爆炸");
}
}, 5, 2, TimeUnit.SECONDS);
}
}
2.3、线程池统一命名:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class ThreadMain {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2, new CustomThreadFactory());
for (int i = 0; i < 10; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
executorService.shutdown();
}
public static class CustomThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
String threadName = "自定义线程名称";
t.setName(threadName);
return t;
}
}
}
3、ThreadLocal 线程本地变量:
ThreadLocal的原理:每个Thread内部维护着一个ThreadLocalMap,它是一个Map。这个映射表的Key是一个弱引用,其实就是ThreadLocal本身,Value是真正存的线程变量Object。
package com.busy.lock.test;
public class ThreadMainTest {
public static void main(String[] args) {
ThreadLocalThread str = new ThreadLocalThread();
Thread t1 = new Thread(str,"窗口1");
Thread t2 = new Thread(str,"窗口2");
Thread t3 = new Thread(str,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
package com.busy.lock.test;
public class ThreadLocalThread implements Runnable {
//通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {
public Integer initialValue() {
return 100;
}
};
@Override
public void run() {
while (seqNum.get() > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (seqNum.get()) + "张票");
seqNum.set(seqNum.get()-1);
}
}
}
每个线程的对象独立存在
3.1、ThreadLocal为什么会内存泄漏
https://blog.csdn.net/Dopamy_BusyMonkey/article/details/103564972
4、ThreadPoolExecutor线程池:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class CustomThreadPoolExecutor {
private ThreadPoolExecutor pool = null;
/**
* 线程池初始化方法
*
* corePoolSize 核心线程池大小----10
* maximumPoolSize 最大线程池大小----30
* keepAliveTime 线程池中超过corePoolSize数目的空闲线程最大存活时间----30+单位TimeUnit
* TimeUnit keepAliveTime时间单位----TimeUnit.MINUTES
* workQueue 阻塞队列----new ArrayBlockingQueue<Runnable>(10)====10容量的阻塞队列
* threadFactory 新建线程工厂----new CustomThreadFactory()====定制的线程工厂
* rejectedExecutionHandler 当提交任务数超过maxmumPoolSize+workQueue之和时,
* 即当提交第41个任务时(前面线程都没有执行完,此测试方法中用sleep(100)),
* 任务会交给RejectedExecutionHandler来处理
*/
public void init() {
pool = new ThreadPoolExecutor(
10,
30,
30,
TimeUnit.MINUTES,
new ArrayBlockingQueue<Runnable>(10),
new CustomThreadFactory(),
new CustomRejectedExecutionHandler());
}
public void destory() {
if(pool != null) {
pool.shutdownNow();
}
}
public ExecutorService getCustomThreadPoolExecutor() {
return this.pool;
}
private class CustomThreadFactory implements ThreadFactory {
private AtomicInteger count = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
String threadName = CustomThreadPoolExecutor.class.getSimpleName() + count.addAndGet(1);
System.out.println(threadName);
t.setName(threadName);
return t;
}
}
private class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// System.out.println("error.............");
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
CustomThreadPoolExecutor exec = new CustomThreadPoolExecutor();
exec.init();
ExecutorService pool = exec.getCustomThreadPoolExecutor();
for(int i=1; i<100; i++) {
System.out.println("提交第" + i + "个任务!");
pool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("running====="+Thread.currentThread().getName());
}
});
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
方法中建立一个核心线程数为30个,缓冲队列有10个的线程池。每个线程任务,执行时会先睡眠3秒,保证提交10任务时,线程数目被占用完,再提交30任务时,阻塞队列被占用完,,这样提交第41个任务是,会交给CustomRejectedExecutionHandler 异常处理类来处理。
注意:41以后提交的任务就不能正常处理了,因为,execute中提交到任务队列是用的offer方法,如上面代码,这个方法是非阻塞的,所以就会交给CustomRejectedExecutionHandler 来处理,所以对于大数据量的任务来说,这种线程池,如果不设置队列长度会OOM,设置队列长度,会有任务得不到处理,接下来我们构建一个阻塞的自定义线程池 。
当提交任务被拒绝时,进入拒绝机制,我们实现拒绝方法,把任务重新用阻塞提交方法put提交,实现阻塞提交任务功能,防止队列过大,OOM,提交被拒绝方法在下面。
线程池线程的创建逻辑:
核心线程数(corePoolSize):核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理。需要注意的是:在刚刚创建ThreadPoolExecutor的时候,线程并不会立即启动,而是要等到有任务提交时才会启动,除非调用了prestartCoreThread/prestartAllCoreThreads事先启动核心线程。再考虑到keepAliveTime和allowCoreThreadTimeOut超时参数的影响,所以没有任务需要执行的时候,线程池的大小不一定是corePoolSize。
最大线程数(maxPoolSize):当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。这里值得一提的是largestPoolSize,该变量记录了线程池在整个生命周期中曾经出现的最大线程个数。为什么说是曾经呢?因为线程池创建之后,可以调用setMaximumPoolSize()改变运行的最大线程的数目。
当前线程数(poolSize):线程池中当前线程的数量,当该值为0的时候,意味着没有任何线程,线程池会终止;同一时刻,poolSize不会超过maximumPoolSize。
线程池按以下行为执行任务:
- 如果当前线程池的线程数还没有达到基本大小(poolSize < corePoolSize),无论是否有空闲的线程新增一个线程处理新提交的任务;
- 如果当前线程池的线程数大于或等于基本大小(poolSize >= corePoolSize) 且任务队列未满时,就将新提交的任务提交到阻塞队列排队,等候处理workQueue.offer(command);
- 如果当前线程池的线程数大于或等于基本大小(poolSize >= corePoolSize) 且任务队列满时;当前poolSize<maximumPoolSize,那么就新增线程来处理任务;当前poolSize=maximumPoolSize,那么意味着线程池的处理能力已经达到了极限,此时需要拒绝新增加的任务。至于如何拒绝处理新增的任务,取决于线程池的饱和策略RejectedExecutionHandler。
5、Semaphore
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。
Semaphore分为单值和多值两种,前者只能被一个线程获得,后者可以被若干个线程获得。
以一个停车场运作为例。为了简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆不受阻碍的进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入一辆,如果又离开两辆,则又可以放入两辆,如此往复。
在java中,还可以设置该信号量是否采用公平模式,如果以公平方式执行,则线程将会按到达的顺序(FIFO)执行,如果是非公平,则可以后请求的有可能排在队列的头部。
JDK中定义如下:
Semaphore(int permits, boolean fair) 创建具有给定的许可数和给定的公平设置的Semaphore
public class SemaphoreTest {
public static void main(String[] args) {
// 线程池
ExecutorService exec = Executors.newCachedThreadPool();
// 只能5个线程同时访问
final Semaphore semp = new Semaphore(5);
// 模拟20个客户端访问
for (int index = 0; index < 50; index++) {
final int NO = index;
Runnable run = new Runnable() {
public void run() {
try {
// 获取许可
semp.acquire();
System.out.println("Accessing: " + NO);
Thread.sleep((long) (Math.random() * 10000));
// 访问完后,释放
semp.release();
//availablePermits()指的是当前信号灯库中有多少个可以被使用
System.out.println("-----------------" + semp.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
exec.execute(run);
}
// 退出线程池
exec.shutdown();
}
}
Accessing: 0
Accessing: 1
Accessing: 2
Accessing: 4
Accessing: 6
Accessing: 8
-----------------0
-----------------1
Accessing: 3
-----------------1
Accessing: 5
Accessing: 9
-----------------0
-----------------1
Accessing: 7
Accessing: 10
-----------------0
-----------------1
Accessing: 11
-----------------1
Accessing: 12
-----------------1
Accessing: 13
Accessing: 14
-----------------0
-----------------1
Accessing: 15
-----------------0
Accessing: 16
-----------------1
Accessing: 17
-----------------1
Accessing: 18
-----------------1
Accessing: 19
-----------------0
Accessing: 20
Accessing: 21
-----------------0
Accessing: 22
-----------------0
-----------------1
Accessing: 23
-----------------1
Accessing: 24
-----------------0
Accessing: 25
Accessing: 26
-----------------0
-----------------1
Accessing: 27
-----------------1
Accessing: 28
-----------------1
Accessing: 29
Accessing: 30
-----------------0
-----------------1
Accessing: 31
-----------------1
Accessing: 32
-----------------1
Accessing: 33
-----------------1
Accessing: 34
Accessing: 35
-----------------0
-----------------1
Accessing: 36
-----------------1
Accessing: 37
-----------------1
Accessing: 38
-----------------1
Accessing: 39
-----------------1
Accessing: 40
Accessing: 41
-----------------0
-----------------1
Accessing: 42
Accessing: 43
-----------------0
Accessing: 44
-----------------0
-----------------1
Accessing: 45
-----------------1
Accessing: 46
-----------------1
Accessing: 47
-----------------1
Accessing: 48
-----------------1
Accessing: 49
-----------------1
-----------------2
-----------------3
-----------------4
-----------------5
6、Callable<V>、Future<V>和FutureTask<V>用法与线程池的连用:
Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值。用法比较灵活,不同的组合可以实现不同的业务效果,当然很多组合实现的效果其实是一样的。
6.1、Future<V>和Callable<V>与线程池获取执行结果:
package com.busy.lock.test;
import java.util.concurrent.Callable;
public class CallableThread implements Callable<Integer> {
private Integer tickets = 100;
@Override
public Integer call() throws Exception {
while (tickets > 0) {
try {
doMethod();
}
}
return tickets;
}
private synchronized void doMethod() {
if (tickets-- > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
}
}
package com.busy.lock.test;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadMainTest {
public static void main(String[] args) {
ScheduledExecutorService schedulePool = Executors.newScheduledThreadPool(1);
CallableThread callableThread = new CallableThread();
Future<Integer> future = schedulePool.schedule(callableThread, 1000, TimeUnit.MILLISECONDS);
//这样就是直接提交,跟定时的0延迟任务一样
//Future<Integer> future = schedulePool.submit(callableThread);
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
schedulePool.shutdown();
}
}
}
6.2、Callable<V>和FutureTask<V>,闭包操作:
package com.busymonkey.concurrent;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableAndFuture {
public static void main(String[] args) {
Callable<Integer> callable = new Callable<Integer>() {
public Integer call() throws Exception {
return new Random().nextInt(100);
}
};
FutureTask<Integer> future = new FutureTask<Integer>(callable);
new Thread(future).start();
try {
Thread.sleep(5000);// 可能做一些事情
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
6.3、Callable<V>和Future<V>,线程池提交闭包操作:
public class CallableAndFuture {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<Integer> future = threadPool.submit(new Callable<Integer>() {
public Integer call() throws Exception {
return new Random().nextInt(100);
}
});
try {
Thread.sleep(5000);// 可能做一些事情
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finall {
threadPool.shutdown();
}
}
}
6.4、批处理任务
public class CallableAndFuture {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);
for(int i = 1; i < 5; i++) {
final int taskID = i;
cs.submit(new Callable<Integer>() {
public Integer call() throws Exception {
return taskID;
}
});
}
// 可能做一些事情
for(int i = 1; i < 5; i++) {
try {
System.out.println(cs.take().get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
threadPool.shutdown();
}
}
7、ForkJoinPool:
ForkJoinPool:这个类实现了ExecutorService接口和工作窃取算法(Work-Stealing Algorithm)。它管理工作者线程,并提供任务的状态信息,以及任务的执行信息。
RecursiveAction:用于任务没有返回结果的场景。
RecursiveTask:用于任务有返回结果的场景。
package com.busymonkey.concurrent;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit;
public class ForkJoinPoolTest {
public static void main(String[] args) throws InterruptedException {
ForkJoinPool pool = new ForkJoinPool();
pool.submit(new PrintTask(1, 100));
pool.awaitTermination(2, TimeUnit.SECONDS);// 阻塞当前线程直到 ForkJoinPool
//中所有的任务都执行结束
pool.shutdown();
}
}
class PrintTask extends RecursiveAction {
private static final long serialVersionUID = 8635119133774500468L;
private int start;
private int end;
private int num;
final int MAX = 50;
public PrintTask(int start, int end) {
this.start = start;
this.end = end;
}
protected void compute() {
if (end - start < 50) {
for (int i = start; i <= end; i++) {
num += i;
}
System.out.println("当前任务结果为: " + num);
} else {
int mid = (end + start) / 2;
PrintTask left = new PrintTask(start, mid);
PrintTask right = new PrintTask(mid + 1, end);
left.fork();
right.fork();
}
}
}
package com.busymonkey.concurrent;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;
public class ForkJoinPoolTask {
public static void main(String[] args) throws InterruptedException {
Integer result = 0;
ForkJoinPool pool = new ForkJoinPool();
Future<Integer> future = pool.submit(new PrintTask1(1, 9999));
try {
result = future.get();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("当前任务结果为: " + result);
pool.shutdownNow();
//pool.shutdown();
}
}
class PrintTask1 extends RecursiveTask<Integer> {
private static final long serialVersionUID = 8635119133774500468L;
private int start;
private int end;
private int num;
final int MAX = 50;
public PrintTask1(int start, int end) {
this.start = start;
this.end = end;
}
protected Integer compute() {
if (end - start < 20) {
for (int i = start; i <= end; i++) {
num += i;
}
System.out.println("当前任务结果为: " + num);
return num;
} else {
int mid = (end + start) / 2;
PrintTask1 left = new PrintTask1(start, mid);
PrintTask1 right = new PrintTask1(mid + 1, end);
left.fork();
right.fork();
return left.join() + right.join();
}
}
}
线程的暂停有两个方法 shutdown 与shutdownNow 两个方法的调用都会阻止新任务的提交,区别是关于已经提交未完成任务的处理已经线程终端的处理,shutdown会继续执行并且完成所有未执行的任务,shutdownNow 会清楚所有未执行的任务并且在运行线程上调用interrupt() 。
8、java Actor:
pom依赖:
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.11.8</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.11</artifactId>
<version>2.3.9</version>
</dependency>
简单测试代码:
package com.busymonkey.actor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
public class ActorSystemTools {
private static ActorSystem actorSystem = null;
public static void start() {
System.out.println("start actorSystem...");
actorSystem = ActorSystem.create();
}
@SuppressWarnings("rawtypes")
public static ActorRef actorOf(Class clazz) {
return actorSystem.actorOf(Props.create(clazz));
}
public static void shutdown() {
System.out.println("shutdown actorSystem...");
actorSystem.shutdown();
}
}
package com.busymonkey.actor;
import akka.actor.UntypedActor;
public class AngryFoalActor extends UntypedActor {
@Override
public void onReceive(Object message) throws Exception {
System.out.println("AngryFoalActor receive message : " + message);
getSender().tell("hello! I am AngryFoalActor!", getSelf());
}
}
package com.busymonkey.actor;
import akka.actor.UntypedActor;
public class LazyFoalActor extends UntypedActor {
@Override
public void onReceive(Object message) throws Exception {
System.out.println("LazyFoalActor receive message : " + message);
}
}
package com.busymonkey.actor;
import akka.actor.ActorRef;
public class ActorTest {
public static void main(String[] args) {
ActorSystemTools.start();
ActorRef angryFoal = ActorSystemTools.actorOf(AngryFoalActor.class);
ActorRef lazyFoal = ActorSystemTools.actorOf(LazyFoalActor.class);
angryFoal.tell("hello! I am LazyFoalActor!", lazyFoal);
}
}
结果:
start actorSystem...
AngryFoalActor receive message : hello! I am LazyFoalActor!
LazyFoalActor receive message : hello! I am AngryFoalActor!
9、阻塞队列LinkedBlockingQueue:
package com.busymonkey.concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
public class LinkedBlockingQueueTest {
// 阻塞队列,FIFO
private static LinkedBlockingQueue<Integer> concurrentLinkedQueue = new LinkedBlockingQueue<Integer>();
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(new Producer("producer1"));
executorService.submit(new Producer("producer2"));
executorService.submit(new Producer("producer3"));
executorService.submit(new Consumer("consumer1"));
executorService.submit(new Consumer("consumer2"));
executorService.submit(new Consumer("consumer3"));
}
static class Producer implements Runnable {
private String name;
public Producer(String name) {
this.name = name;
}
public void run() {
for (int i = 1; i < 10; ++i) {
System.out.println(name + " 生产: " + i);
// concurrentLinkedQueue.add(i);
try {
concurrentLinkedQueue.put(i);
Thread.sleep(200); // 模拟慢速的生产,产生阻塞的效果
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}
static class Consumer implements Runnable {
private String name;
public Consumer(String name) {
this.name = name;
}
public void run() {
for (int i = 1; i < 10; ++i) {
try {
// 必须要使用take()方法在获取的时候阻塞
System.out.println(name + " 消费: " + concurrentLinkedQueue.take());
// 使用poll()方法 将产生非阻塞效果
// System.out.println(name+"消费: " +
// concurrentLinkedQueue.poll());
// 还有一个超时的用法,队列空时,指定阻塞时间后返回,不会一直阻塞
// 但有一个疑问,既然可以不阻塞,为啥还叫阻塞队列?
// System.out.println(name+" Consumer " +
// concurrentLinkedQueue.poll(300, TimeUnit.MILLISECONDS));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
10、非阻塞队列ConcurrentLinkedQueue:
package com.busymonkey.concurrent;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ConcurrentLinkedQueueTest {
private static ConcurrentLinkedQueue<Integer> concurrentLinkedQueue = new ConcurrentLinkedQueue<Integer>();
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(new Producer("producer1"));
executorService.submit(new Producer("producer2"));
executorService.submit(new Producer("producer3"));
executorService.submit(new Consumer("consumer1"));
executorService.submit(new Consumer("consumer2"));
executorService.submit(new Consumer("consumer3"));
}
static class Producer implements Runnable {
private String name;
public Producer(String name) {
this.name = name;
}
public void run() {
for (int i = 1; i < 10; ++i) {
System.out.println(name + " start producer " + i);
concurrentLinkedQueue.add(i);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Consumer implements Runnable {
private String name;
public Consumer(String name) {
this.name = name;
}
public void run() {
for (int i = 1; i < 10; ++i) {
try {
if (!concurrentLinkedQueue.isEmpty()) {
System.out.println(name + " Consumer " + concurrentLinkedQueue.poll());
} else {
System.out.println(name + " not cost");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
使用阻塞队列的好处:多线程操作共同的队列时不需要额外的同步,另外就是队列会自动平衡负载,即那边(生产与消费两边)处理快了就会被阻塞掉,从而减少两边的处理速度差距。
当许多线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择。
LinkedBlockingQueue 多用于任务队列
ConcurrentLinkedQueue 多用于消息队列
多个生产者,对于LBQ性能还算可以接受;但是多个消费者就不行了mainLoop需要一个timeout的机制,否则空转,cpu会飙升的。LBQ正好提供了timeout的接口,更方便使用
如果CLQ,那么我需要收到处理sleep
单生产者,单消费者 用 LinkedBlockingqueue
多生产者,单消费者 用 LinkedBlockingqueue
单生产者 ,多消费者 用 ConcurrentLinkedQueue
多生产者 ,多消费者 用 ConcurrentLinkedQueue
11、CountDownLatch线程协调:
CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成了任务,然后在CountDownLatch上等待的线程就可以恢复执行任务。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainTest {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(3);
final CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println("子线程" + Thread.currentThread().getName() + "开始执行");
Thread.sleep((long) (Math.random() * 10000));
System.out.println("子线程" + Thread.currentThread().getName() + "执行完成");
latch.countDown(); // 当前线程调用此方法,则计数减一
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
System.out.println("主线程" + Thread.currentThread().getName() + "等待子线程执行完成...");
latch.await(); // 阻塞当前线程,直到计时器的值为0
System.out.println("主线程" + Thread.currentThread().getName() + "开始执行...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}