java 多线程框架

目录

1、Thread和Runnable:

1.1、继承Thread:

1.2、实现Runnable接口:

1.3、Thread和Runnable闭包创建线程:

2、ExecutorService :

2.1、瞬时线程:

2.2、定时线程和周期线程:

2.3、线程池统一命名:

3、ThreadLocal 线程本地变量:

3.1、ThreadLocal为什么会内存泄漏

4、ThreadPoolExecutor线程池:

5、Semaphore

6、Callable、Future和FutureTask用法与线程池的连用:

6.1、Future和Callable与线程池获取执行结果:

6.2、Callable和FutureTask,闭包操作:

6.3、Callable和Future,线程池提交闭包操作:

6.4、批处理任务

7、ForkJoinPool:

8、java Actor:

9、阻塞队列LinkedBlockingQueue:

10、非阻塞队列ConcurrentLinkedQueue:

11、CountDownLatch线程协调:


 

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();
		}
	}

}

 

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值