多线程和并发库
多线程基础
线程创建
(1)使用用Thread和接口Runnable实现
- 在Thread子类覆盖Thread的run方法
方式一
new Thread(){
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
方式二
new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
总结:查看Thread类的run()方法的源代码,可以看到其实这两种方式否是在调用Thread对象的run()方法,如果Thread类的run方法没有被覆盖,并且为Thread对象设置了一个Runnable对象,该run方法会调用Runnable对象的run方法
(2)实现定时器Timer和TimerTask
Timer在实际开发中应用场景不多,一般来说都会用其第三方库来实现,但有时会在一些面试题中出现。例如以下:
模拟写出双重定时器
public class ThreadTest01 {
//要求,使用定时器,间隔4秒执行一次,再间隔2秒执行一次,以此类推
private static volatile int count = 0;
static class TimerTastCus extends TimerTask{
@Override
public void run() {
count = (count+1)%2;
System.out.println("Boob boom");
new Timer().schedule(new TimerTastCus(),2000+2000*count);
}
}
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTastCus(),2000+2000*count);
while (true){
System.out.println(new Date().getSeconds());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程互斥与同步
- 间接互相制约:一个系统中的多个线程必然共享某种系统资源,如共享CPU,共享IO设备,所谓间接相互制约即源于资源共享,打印机就是最好的例子,线程A正在使用打印机时,其他线程都要等待。
- 直接相互制约:主要是因为线程之间的合作,如有线程A将计算结果提供给线程B做进一步处理,那么线程B在线程A将数据到达之前处于阻塞状态。
间接相互制约称为互斥,直接相互制约称为同步。
要求:子线程运行10次后,主线程在运行5次,交替运行三遍
public class ThreadTest02 {
public static void main(String[] args) {
//要求子线程运行10次后,主线程再运行5次,交替运行三次
final Bussiness bussiness = new Bussiness();
//子线程
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
bussiness.subMethod();
}
}
}).start();
//主线程
for (int i = 0; i < 3; i++) {
bussiness.mainMethod();
}
}
}
public class Bussiness {
private boolean subFlag = true;
public synchronized void mainMethod(){
while (subFlag){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()
+":main thread running loop count --"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
subFlag=true;
notify();
}
public synchronized void subMethod(){
while (!subFlag){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()
+":sub thread running loop count --"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
subFlag=false;
notify();
}
}
线程局部变量ThreadLoacal
ThreadLoacl的作用和目的:用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个线程中运行时共享一份数据,而在另外线程中运行时又共享另外一份数据。
每个线程调用全局ThreadLocal对象的set方法,在set方法中,首先根据当前线程获取当前线程的ThreadLocalMap对象,然后往这个map中插入一条记录,key其实是ThreadLocal对象,value是各自的set方法传进去的值。也就是每个线程其实都有一份自己独享的ThreadLocalMap对象,该对象的key是ThreadLocal对象,值是用户设置的具体值。在线程结束时可以调用ThreadLocal.remove()方法,这样会更快释放内存,不调用也可以,因为线程结束也可以自动释放相关的ThreadLocal变量。
ThreadLocal应用场景
订单处理的一系列操作:减少库存量,增加一条流水台账,修改总账,这几个操作要在同一个事务中完成,即同一个线程中进行处理。
银行转账包含一系列操作:把转出账户的余额减少,把转入账户的余额增加,这两个操作要在一个业务中完成,他们必须使用相同的数据库连接对象,转入和转出操作的代码分别是两个不同的账户对象的方法。
ThreadLocal的使用方式
在关联数据类中创建private staticThreadLocal
在下面的类中,私有静态 ThreadLocal 实例(serialNum)为调用该类的静态 SerialNum.get() 方法的每个线程维护了一个“序列号”,该方法将返回当前线程的序列号。(线程的序列号是在第一次调用 SerialNum.get() 时分配的,并在后续调用中不会更改。)
public class SerialNum {
private static int nextSerialNum = 0;
private static ThreadLocal serialNum = new ThreadLocal(){
@Override
protected synchronized Object initialValue() {
return new Integer(nextSerialNum);
}
};
public static int get(){
return ((Integer)(serialNum.get())).intValue();
}
}
public class ThreadContext {
private String userId;
private Long transactionId;
private static ThreadLocal threadLocal = new ThreadLocal(){
@Override
protected ThreadContext initialValue() {
return new ThreadContext();
}
};
public static ThreadContext get(){
return (ThreadContext) threadLocal.get();
}
public void setUserId(String userId) {
this.userId = userId;
}
public Long getTransactionId() {
return transactionId;
}
public void setTransactionId(Long transactionId) {
this.transactionId = transactionId;
}
public static ThreadLocal getThreadLocal() {
return threadLocal;
}
public static void setThreadLocal(ThreadLocal threadLocal) {
ThreadContext.threadLocal = threadLocal;
}
public String getUserId(){
return userId;
}
}
在Runnbale中创建ThreadLocal
在线程类内部创建ThreadLocal,基本步骤:
- 在多线程的类(如ThreadDemo类),创建一个ThreadLocal对象ThreadXxx,用来保存线程间需要间隔处理的对象xxx
- 在ThreadDemo类中,创建一个获取要间隔访问的数据的方法getXxx(),在方法中判断,若ThreadLocal对象为null,应该new()一个隔离访问类型的对象,并强制转换为要对应的类型
- 在ThreadDemo类的run方法中,通过调用getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻操作的是这个对象
public class ThreadLocalDemo implements Runnable {
ThreadLocal<Student>studentThreadLocal = new ThreadLocal<Student>();
@Override
public void run() {
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName+"is running...");
Random random = new Random();
int age = random.nextInt(100);
System.out.println(currentThreadName+"is set age:"+age);
Student student = getStudent();
student.setAge(age);
System.out.println(currentThreadName+"is first get age:"+student.getAge());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(currentThreadName+"is second get age:"+student.getAge());
}
private Student getStudent(){
Student student = studentThreadLocal.get();
if (null == student){
student = new Student();
studentThreadLocal.set(student);
}
return student;
}
public static void main(String[] args) {
ThreadLocalDemo tld = new ThreadLocalDemo();
Thread t1 = new Thread("Thread A");
Thread t2 = new Thread("Thread B");
t1.start();
t2.start();
}
}
public class Student {
int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
多线程共享数据
在java传统线程机制中的共享数据方式,大致分为两种情况
- 多个线程行为一致,共同操作一个数据源,也就是每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,卖票系统就可以这么做。
- 多个线程行为不一致,共同操作一个数据源。也就是每个线程执行的代码不同,这时候需要不同的Runnable对象。例如,银行存取款。
多个线程行为一致共同操作一个数据
如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如卖票系统。
public class TicketTest {
/**
* 共享数据
*/
static class ShareData{
private int num = 10;
public synchronized void inc(){
num++;
System.out.println(Thread.currentThread().getName()+":invoke inc method num="+num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 多线程
*/
static class RunnableCusToInc implements Runnable{
private ShareData shareData;
public RunnableCusToInc(ShareData shareData){
this.shareData=shareData;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
shareData.inc();
}
}
}
public static void main(String[] args) {
ShareData shareData = new ShareData();
for (int i = 0; i < 4; i++) {
new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
}
}
}
多个线程行为不一致共同操作一个数据
如果多个线程执行的代码不同,这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:
- 将共享数据封装在另外一个对象中,然后将这个对象逐一传递给Runnable对象,每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
package test;
public class TicketTest {
/**
* 共享数据
*/
static class ShareData{
private int num = 10;
public synchronized void inc(){
num++;
System.out.println(Thread.currentThread().getName()+":invoke inc method num="+num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 多线程
*/
static class RunnableCusToInc implements Runnable{
private ShareData shareData;
public RunnableCusToInc(ShareData shareData){
this.shareData=shareData;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
shareData.inc();
}
}
}
public static void main(String[] args) {
ShareData shareData = new ShareData();
for (int i = 0; i < 4; i++) {
// new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
if (i%2 == 0){
new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
}else {
new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
}
}
}
}
- 将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象用外部类的这些方法。
/**
* 共享数据
*/
static class ShareData{
private int num = 10;
public synchronized void inc(){
num++;
System.out.println(Thread.currentThread().getName()+":invoke inc method num="+num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void dec(){
num--;
System.out.println(Thread.currentThread().getName()+":invoke inc method num="+num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ShareData shareData = new ShareData();
/* for (int i = 0; i < 4; i++) {
// new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
if (i%2 == 0){
new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
}else {
new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
}
}*/
for (int i = 0; i < 4; i++) {
if (i%2 == 0){
new Thread(new Runnable() {
@Override
public void run() {
for (int i1 = 0; i1 < 5; i1++) {
shareData.inc();
}
}
},"Thread"+i).start();
}else {
new Thread(new Runnable() {
@Override
public void run() {
for (int i1 = 0; i1 < 5; i1++) {
shareData.inc();
}
}
},"thread"+i).start();
}
}
}
上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程共享数据的操作方法也分配到那个对象身上完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。
总之,要同步互斥的极端代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现他们之间的同步互斥和通信。
Java的并发库
java.util.concurrent包描述:
在并发编程中很常用的实用工具类。此包包括了几个小的,已标准化的可扩展框架,以及以及一些提供有用功能的类。此包下的一些组件,其包括:
执行程序(线程池)
并发队列
同步器
开发Collection
执行程序
Executors线程池工厂类
作用:限制系统中执行线程的数量,根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;减少了系统资源的浪费,为什么使用线程池
减少创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务
可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,而把服务累趴下。Executors详解
java里线程池的顶级接口,严格意义上讲Executor并不是一个线程池,而是一个执行线程的工具。真正的线程池接口是ExecutorService。ThreadPoolExecutor是Executors类的底层实现。
线程池的基本思想是一种对象池的思想,开辟一块内存空间,里面存放了众多的线程,池中线程执行调度有池管理器来处理。当有任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
java5中并发库中,线程池创建方法
//创建固定大小的线程池
ExecutorService fPool = Executors.newFixedThreadPool(3);
//创建缓存大小的线程池
ExecutorService cPool = Executors.newCachedThreadPool();
//创建单一的线程池
ScheduledExecutorService sPool = Executors.newSingleThreadScheduledExecutor();
固定大小连接池
public class ThreadPoolTest01 {
public static void main(String[] args) {
//固定大小连接池
//创建一个可重用固定线程数的线程池
ExecutorService fPool = Executors.newFixedThreadPool(2);
//创建一个使用单个worker线程的Executor,以无界队列方式来运行该线程
//ScheduledExecutorService sPool = Executors.newSingleThreadScheduledExecutor();
//可变连接池
//创建一个使用单个worker线程的Executor,以无界队列方式来运行该线程
//ExecutorService cPool = Executors.newCachedThreadPool();
//延迟连接池
//创建一个线程池,他可以安排给定延迟后运行命令或者定期地执行
//ScheduledExecutorService Pool = Executors.newScheduledThreadPool(2);
//创建实现了Runnable接口对象,Thread对象当然也要实现Runnable接口
myThread t1 = new myThread();
myThread t2 = new myThread();
myThread t3 = new myThread();
myThread t4 = new myThread();
//将线程放入池中进行执行
fPool.execute(t1);
fPool.execute(t2);
fPool.execute(t3);
fPool.execute(t4);
//关闭线程池
fPool.shutdown();
/* sPool.execute(t1);
sPool.execute(t2);
sPool.execute(t3);
sPool.execute(t4);
sPool.shutdown();*/
/* cPool.execute(t1);
cPool.execute(t2);
cPool.execute(t3);
cPool.execute(t4);
cPool.shutdown();*/
/* Pool.execute(t1);
Pool.execute(t2);
Pool.execute(t3);
Pool.execute(t4);
Pool.shutdown();*/
}
/**
* 创建自己线程
*/
static class myThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行。。。");
}
}
}
单任务连接池
ScheduledExecutorService sPool = Executors.newSingleThreadScheduledExecutor();
//创建实现了Runnable接口对象,Thread对象当然也要实现Runnable接口
myThread t1 = new myThread();
myThread t2 = new myThread();
myThread t3 = new myThread();
myThread t4 = new myThread();
sPool.execute(t1);
sPool.execute(t2);
sPool.execute(t3);
sPool.execute(t4);
sPool.shutdown();
可变连接池
//可变连接池
//创建一个使用单个worker线程的Executor,以无界队列方式来运行该线程
ExecutorService cPool = Executors.newCachedThreadPool();
//创建实现了Runnable接口对象,Thread对象当然也要实现Runnable接口
myThread t1 = new myThread();
myThread t2 = new myThread();
myThread t3 = new myThread();
myThread t4 = new myThread();
cPool.execute(t1);
cPool.execute(t2);
cPool.execute(t3);
cPool.execute(t4);
cPool.shutdown();
延迟连接池
//延迟连接池
//创建一个线程池,他可以安排给定延迟后运行命令或者定期地执行
ScheduledExecutorService Pool = Executors.newScheduledThreadPool(2);
//创建实现了Runnable接口对象,Thread对象当然也要实现Runnable接口
myThread t1 = new myThread();
myThread t2 = new myThread();
myThread t3 = new myThread();
myThread t4 = new myThread();
Pool.execute(t1);
Pool.execute(t2);
Pool.execute(t3);
Pool.execute(t4);
Pool.shutdown();
ExecutorService执行器服务
java.util.concurrent.ExecutorService接口表示一个异步执行机制,使我们能够在后台执行任务,一次一个ExecutorService很类似于一个线程池,实际上,存在于java.util.concurrent包里的ExecutorService实现就是一个线程池实现
ExecutorService实例
public class ExecutorServiceTest {
public static void main(String[] args) {
// 线程工厂类创建出线程池
ExecutorService fPool = Executors.newFixedThreadPool(10);
//执行线程
fPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("Asynchronus task...");
}
});
//关闭线程池
fPool.shutdown();
}
}
首先使用newFixedThreadPool()工厂方法创建了一个ExecutorService,这里创建了一个十个线程执行任务的线程池。然后,讲一个Runnable接口的匿名实现类传递给execute()方法,这将导致ExecutorService中的某个线程执行Runnable。这里可以看成一个任务分派,
一个任务将一个任务委派给一个ExecutorService去异步执行
一旦该线程将任务委派给ExecutorService,该线程将继续它自己的执行,独立于该任务的执行。
ExecutorService实现
java.util.concurrent包提供了ExecutorService接口的以下实现类:
ThreadPoolExecutor
ScheduledThreadPoolExecutor
ExecutorService创建
ExecutorService的创建依赖于使用的具体实现,但是也可以使用Executors工厂类来创建,例如以下创建方式:
//创建固定大小的线程池
ExecutorService fPool = Executors.newFixedThreadPool(3);
//创建缓存大小的线程池
ExecutorService cPool = Executors.newCachedThreadPool();
//创建单一的线程池
ScheduledExecutorService sPool = Executors.newSingleThreadScheduledExecutor();
ExecutorService使用
有几种不同的方式来将任务委托给ExecutorService去执行:
execute(Runnable)
submit(Runnable)
submit(Callable)
invokeAny(…)
invokeAll(…)
execute(Runnable)
要求实现一个java.lang.Runnable对象,然后对它进行异步执行
public class ExecuteTest {
public static void main(String[] args) {
//从Executors中获取ExecutorService
ExecutorService executorService = Executors.newSingleThreadExecutor();
//执行executorService
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("Asynchronize task");
}
});
//关闭线程池
executorService.shutdown();
}
}
submit(Runnable)
submit(Runnable)方法也要要求一个Runnable实现类,但是它返回Future对象。这个Future对象可以用来检查Runnable是否已经执行完毕,以下是ExecutorService。submit()实例:
public class SubmitTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//从Executors中获取ExecutorService
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("Asynchronous task");
}
});
//获得执行完run方法后的返回值
future.get();
executorService.shutdown();
}
}
submit(Callable)
submit(Callable)方法类似于submit(Runnable)方法,除了所要求的参数类型之外,Callable实例除了它的call()方法能返回一个结果之外和一个Runnable很相像。Runable.run()不能够返回一个结果。Callable的结果可以通过submit(Callable)方法返回的Future对象进行获取,以下是ExecutorService Callable实例:
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//从Executors中获得ExecutorService
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(new Callable(){
@Override
public Object call() {
System.out.println("Asynchronous Callable");
return "Callable Result";
}
});
System.out.println("future.get()="+future.get());
executorService.shutdown();
}
}
invokeAny()
invokeAny()方法要求一系列的Callable或者其子接口的实例对象。调用这个方法并不会返回一个Future,但是它返回一个Callable对象的结果。无法保证返回的是哪个Callable的结果-只能表明其中一个已执行结束。如果其中一个任务执行结束(或者抛出了一个异常),其他Callable将被取消,以下是实例:
public class InvokeAnyTest {
public static void main(String[] args){
ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<>();
callables.add(new Callable<String>(){
@Override
public String call() throws Exception{
return "task 1";
}
});
callables.add(new Callable<String>() {
@Override
public String call() throws Exception {
return "task 2";
}
});
String result = null;
try {
result = executorService.invokeAny(callables);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("result="+result);
executorService.shutdown();
}
}
invokeAll()
invokeAll()方法将调用你在集合中传给ExecutorService的所有Callable对象。invokeAll()返回一系列的Future对象,通过他们可以获取每个Callable的执行结果。以下是实例:
public class InvokeAllTest {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<>();
callables.add(new Callable<String>() {
@Override
public String call() throws Exception {
return "task 1";
}
});
callables.add(new Callable<String>() {
@Override
public String call() throws Exception {
return "task 2";
}
});
List<Future<String>> futures = null;
try {
futures = executorService.invokeAll(callables);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (Future<String> future : futures) {
System.out.println("future get="+future);
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
executorService.shutdown();
}
}
shutdown和shutdownNow区别
shutdown
将空闲的线程interrupt,shutdown()之前提交的任务可以继续执行直到结束
shutdownNow
是interrupt所有线程,因此大部分线程立刻被中断,之所以是大部分,而不是全部,是因为interrupt()方法能力有限。
ThreadPoolExecutor线程池执行者
java.util.current.ThreadPoolExecutor是ExecutorService接口的一个实现。ThreadPoolExecutor使用其内部池中的线程执行给定任务(Callable或者Runnable)
ThreadPoolExecutor包含的线程池能够包含不同数量的线程,池中线程的数量是由以下变量决定:
corePoolSize
maximumPoolSize
当一个任务委托给线程池时,如果池中线程数量低于corePoolSize,一个新的线程将被创建,即使池中可能尚有空闲线程,如果内部任务队列已满,而且至少corePoolSize正在运行,但是运行线程的数量低于maximumPoolSize,一个线程将被创建去执行该任务。
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
int corePoolSize = 5;
int maxPoolSize = 10;
long keepAliveTime = 5000;
ExecutorService threadPoolExecutor = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()
);
//构造方法参数列表详解
corePoolSize--池中所保持的线程池,包括空闲线程。
maximumPoolSize--池中允许的最大线程数。
keepAliveTime--当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit-keepAliveTime 参数的时间单位 。
workQueue--执行前用于保持任务的队列,次队列仅保持由excute方法提交的Runable任务。
}
}
ScheduledPoolExecutor定时线程池执行者
java.util.concurrent.ScheduledPoolExecutorService是一个ExecutorService,它能够将任务延后执行,或者间隔固定时间多次执行。任务由一个工作者线程异步执行,而不是由提交任务给ScheduedExecutorService的那个线程执行。
public class ScheduledPoolExecutorTest {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
scheduledExecutorService.schedule(new Callable<Object>() {
@Override
public Object call() throws Exception {
System.out.println("Executed");
return "Called";
}
},5, TimeUnit.SECONDS);//5秒后执行
}
}
首先一个内置10个线程的ScheduledExecutorService被创建。之后一个Callable接口的匿名类实例被创建然后传递给schedule()方法,后边的俩参数定义了Callable将在5秒之后执行。
ForkJoinPool合并和分叉(线程池)
java 7 引入,和ExecutorService相似,一点不同就是ForkJoinPool可以方便把任务分裂成几个更小的任务,分裂出来的任务将会交给ForkJoinPool。任务可以继续分割成更小的子任务,只要还能分割。
分叉
一个使用了分叉和合并原理的任务可以将自己分叉为更小的子任务,这些子任务可以并发执行。
合并
当一个任务将自己分割成若干子任务后,该任务将进入等待所有子任务的结束之中,一旦子任务执行结束,该任务可以把所有结果合并到同一个结果。
实现步骤
- 创建一个ForkJoinPool
通过其构造子类创建一个ForkJoinPool。作为传递给ForkJoinPool构造子类的一个参数- 提交任务到ForkJoinPool
就像提交任务到ExecutorService那样,把任务提交到ForkJoinPool,两种提交类型的任务,一种是没有返回值,另一种是由返回值的,这两种分别有RecursiveAction和RecursiveTask表示。- RecursiveAction
是一种没有返回值的任务,只是做一些工作,比如写数据到磁盘,然后退出。例如:
public class MyRecursiveAction extends RecursiveAction {
private long workLoad = 0;
public MyRecursiveAction(long workLoad) {
this.workLoad = workLoad;
}
@Override
protected void compute() {
//如果工作超过门槛,把任务分解成更小的任务
if (this.workLoad>16){
System.out.println("Splitting workLoad:"+this.workLoad);
ArrayList<MyRecursiveAction> subtaks = new ArrayList<>();
subtaks.addAll(createSubtaks());
for (MyRecursiveAction subtak : subtaks) {
subtak.fork();
}
}else {
System.out.println("Doing workLoad myself:"+this.workLoad);
}
}
private List<MyRecursiveAction> createSubtaks() {
List<MyRecursiveAction> subtasks = new ArrayList<>();
MyRecursiveAction subtask1 = new MyRecursiveAction(this.workLoad / 2);
MyRecursiveAction subtask2 = new MyRecursiveAction(this.workLoad / 2);
subtasks.add(subtask1);
subtasks.add(subtask2);
return subtasks;
}
}
MyRecursiveAction将一个虚构的workLoad作为参数传给自己的构造子类,如果workLoad高于一个特定阈值,该工作将被分割为几个子工作,子工作继续分割。如果workLoad低于特定阈值,该工作将由MyRescursiveAction自己执行。可以规划一个MyRecursive的执行:
ForkJoinPool forkJoinPool = new ForkJoinPool(4);
MyRecursiveAction myRecursiveAction = new MyRecursiveAction(24);
forkJoinPool.invoke(myRecursiveAction);
- RecursiveTask
RecursiveTask是一种返回结果的任务。它可以将自己的工作分割为若干更小任务,并将这些子任务的执行结果并到一个集体结果。可以由几个水平的分割和合并,以下是一个RecursiveTask实例:
public class MyRecursiveTask extends RecursiveTask<Long> {
private long workLoad = 0;
public MyRecursiveTask(long workLoad) {
this.workLoad = workLoad;
}
protected Long compute() {
//如果工作超过门槛,把任务分解成更小的任务
if (this.workLoad>16){
System.out.println("Splitting workLoad:"+this.workLoad);
List<MyRecursiveTask> subtasks = new ArrayList<>();
subtasks.addAll(createSubtaks());
for (MyRecursiveTask subtask : subtasks) {
subtask.fork();
}
long result = 0;
for (MyRecursiveTask subtask : subtasks) {
result += subtask.join();
}
return result;
}else {
System.out.println("Doing workLoad myself:"+this.workLoad);
return workLoad*3;
}
}
private List<MyRecursiveTask> createSubtaks() {
List<MyRecursiveTask> subtasks = new ArrayList<>();
MyRecursiveTask subtask1 = new MyRecursiveTask(this.workLoad / 2);
MyRecursiveTask subtask2 = new MyRecursiveTask(this.workLoad / 2);
subtasks.add(subtask1);
subtasks.add(subtask2);
return subtasks;
}
}