快开学了,作为大三的老油条,得赶紧写大二同志们的培训内容了,现在这里打点草稿吧。
先看一下线程池的内容:
线程池
int count = ??? //自己定义一个吧
//long startTime System.currentTimeMillis();
final List<Integer> l = new LinkedList<Integer>();
ThreadPoolExecutor tp = new ThreadPoolExecutor(1,3,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(count));
final Random random = new Random();
for(int i=0;i<count;i++){
tp.execute(new Runnable(){
@override
public void run(){
l.add(random.nextInt()); //random在安全性上并不是真正的随机,这里略过
}
})
}
//新的任务不会再被提交到线程池,但之前的都会依旧执行,通过中断方式停止空闲的(根据没有获取锁来确定)线程。
tp.shutdown();
try{
tp.awaitTermination(1,TimeUnit.DAYS);
//阻塞等待shutdown请求后所有线程终止,会有时间参数,超时和中断也会令方法调用结束。
}catch(InterruptedException e){
e.printStrackTrace();
}
//long endTime System.currentTimeMillis();
对线程池的一些解释:
关于实例化
new ThreadPoolExecutor(1,3,60,TimeUnit.SECONDS,new LinkedBlockingQueue(count));
参数实际上是:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,
BlockingQueue workQueue,RejectedExecutionHandler handler)
- corePoolSize: 线程池维护线程的最少数量
- maximumPoolSize:线程池维护线程的最大数量
- keepAliveTime: 线程池维护线程所允许的空闲时间
- unit: 线程池维护线程所允许的空闲时间的单位
- workQueue: 线程池所使用的缓冲队列
- handler: 线程池对拒绝任务的处理策略 //这个被缺省了
execute(Runnable param):
将任务提交到线程池
execute提交任务的策略:
- 线程池中的数量num < corePoolSize,提交任务被线程实例化,直接处理
- 线程池中的数量num >= corePoolSize && 线程池中的数量num < 缓冲队列workQueue,任务被放到缓冲队列。
- 线程池中的数量num > workQueue && 线程池中的数量num < maximumPoolSize,任务被直接线程化处理
- 线程池中的数量num > maximumPoolSize,handler处理被拒绝的任务。
关于keepAliveTime参数
线程池中的数量num > corePoolSize, 有线程空闲时间超过keepAliveTime,线程将被终止。
关于 unit参数
参数可选值为java.util.concurrent.TimeUnit中的几个静态属性:
- NANOSECONDS、
- MICROSECONDS、
- MILLISECONDS、
- SECONDS。
关于workQueue参数:
- java.util.concurrent.ArrayBlockingQueue
- java.util.concurrent.LinkedBlockingQueue
关于handler参数:
- ThreadPoolExecutor.AbortPolicy() :抛出java.util.concurrent.RejectedExecutionException异常
- ThreadPoolExecutor.CallerRunsPolicy():当抛出RejectedExecutionException异常时,会调用rejectedExecution方法
(如果主线程没有关闭,则主线程调用run方法,源码如下:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
)
- ThreadPoolExecutor.DiscardOldestPolicy() : 抛弃旧的任务
- ThreadPoolExecutor.DiscardPolicy() : 抛弃当前的任务
除了ThreadPoolExecutor,还有定时线程池ScheduledThreadPoolExecutor等
ThreadPoolExecutor的更多解析 : 看 三石 * 道
一般线程操作
int count = ???;
//long startTime = System.currentTimeMillis();
final List<Integer> l= new LinkedList<Integer>();
final Random random = new Random();
for(int i=0;i<count;i++)
{
Thread thread = new Thread(){
@override
public void run(){
l.add(random.nextInt());
}
};
thread.start();
try{
thread.join(); //保证各个线程顺序执行
}catch(InterruptedException e){
e.printStackTrace();
}
}
//long endTime System.currentTimeMillis();
synchronized关键字 :
学习java线程基础,这就应该是个老相识了,
synchronized一般有下面三种:静态方法同步,所有对象的方法调用互斥 : public synchronized static returnType methodName(){}
对象方法同步 , 同一对象的方法调用互斥 : public synchronizedreturnType methodName(){}
代码块的互斥同步,有synchronized(this)和synchronized(ClassName.class),实现更加灵活
建议 : 尽量使用代码块的synchronized,而不是方法上的,最好避免synchronized,在单例模式的懒汉模式中就有讨论。
ReentrantLock和ReentrantReadWriteLock
- ReentrantLock(可重入锁),提供了tryLock() [判断是否有线程持有锁] 和 公平非公平机制,但是需要显式的unlock().
- ReentrantReadWriteLock(可重入读写锁),是对读多写少的一种优化,上过操作系统课程的人应该有很深刻的印象,ReentrantReadWriteLock提供了readLock和writeLock两种方法,具体代码实例 : 这里
- 在线程池的源码实现中,就是用了ReentrantLock
Volatile
这个关键字保证了获取的变量只保存在主存中,永远得到最新值,而synchronized保证了本地副本和主存的同步,一般的方法获取的变量是当前线程的副本。
volatile关键字虽然保证了变量的最新,解决了可见性,但是无法控制并发,需要使用synchronized,相信大家在java学习过程中,就听过volatile不要用的说法
Atomics
在java.util.concurrent.atomic包里面,提供了很多以Atomic开头的类,用于相关的原子操作,替代了synchronized,是代码更简洁,比如
public class TestAtomics{
AtomicInteger count =new AtomicInteger();
public int increase(){
return count.incrementAndGet();
}
public int decrease(){
return count.decrementAndGet();
}
}
用synchronized实现是这样的:
public class TestSync{
int count = 0;
public int increase(){
synchronized(this){
count += 1;
return count;
}
}
public int decrease(){
synchronized(this){
count -= 1;
return count;
}
}
}
除了代码的简介性之外,性能也有了很大的提升,在《大型网站系统和Java中间件实践》一书中,解释为AtomicInteger内部调用了JNI使用了硬件支持的CAS指令。
wait、notify、notifyAll
看过java的Object对象源码的同学应该知道上面三个是Object的三个方法,notify用于唤醒一个等待线程,notifyAll用于唤醒所有的等待线程,wait则是进行等待的,在多线程中,将某个对象作为事件对象,通过上面三个方法完成线程间的状态通知。
CountDownLacth
CountDownLatch提供了这样一个机制 : M个线程等待N个线程都达到了预期状态或者完成了预期的工作时,才被触发,继续自己的后续工作。线程调用ContDownLatch的await方法进行阻塞,到达了预期状态的线程使用countDonw方法计数减一。
CyclicBarrier
CountDownLatch和CyclicBarrier都实现了多个线程之间的协调。CountDownLatch中,执行countDown的线程,执行完countDown之后就继续自己的工作。而在CyclicBarrier中,调用await操作的各个线程只有所有的线程都到了await方法后,才都继续下面的工作。
CyclicBarrier提供了一个runnable的构造方法,用于await后调用,很赞,还有本身的可复用性的设计也很贴心。
CyclicBarrier的代码例子,点击一下
Semaphore
看名字,就想起了操作系统,对吧,是的,在java中,提供了acquire()和release()方法,以及带有int计数的构造函数
Semaphore的代码例子,点击一下
Exchanger
这个类,用于两个线程之间交换数据的,使用Exchanger<数据类型>进行实例化,在需要交换数据的时候,调用exchanger对象.exchange(交换的数据对象);
Exchanger的代码例子,点击一下
Future和FutureTask
如果熟悉异步阻塞的话,Future作为接口,FutureTask作为Future的具体实现类,能够实现异步的工作。
使用Future,常规有一下几种方法:
* 使用Callable构造Future对象
ExecutorService executor = Executors.newSingleThreadExecutor();
FutureTask<String> future = new FutureTask<String>(
new Callable<String>() {//使用Callable接口作为构造参数
public String call() {
//真正的任务在这里执行,这里的返回值类型为String,可以为任意类型
}});
executor.execute(future);
try {
result = future.get(5000, TimeUnit.SECONDS);
//取得结果,同时设置超时执行时间为5秒。同样可以用future.get(),不设置执行超时时间取得结果
} catch (InterruptedException e) {
//取消任务
futureTask.cancel(true);
} catch (ExecutionException e) {
futureTask.cancel(true);
} catch (TimeoutException e) {
futureTask.cancel(true);
} finally {
executor.shutdown();
}
- 获取执行结果
ExecutorService executor = Executors.newSingleThreadExecutor();
FutureTask<String> future = executor.submit(
new Callable<String>() {
//使用Callable接口作为构造参数
public String call() {
//真正的任务在这里执行,这里的返回值类型为String,可以为任意类型
}});
- 补充callable和runnable的差别
1.Callable 使用 call() 方法, Runnable 使用 run() 方法
2.call() 可以返回值, 而 run()方法不能返回。
3.call() 可以抛出受检查的异常,比如ClassNotFoundException, 而run()不能抛出受检查的异常。
具体实现代码,点击这里
*补充Executors和ExecutorServce:
看看这个家伙的博文吧 , 设计源代码, 点击进入
写的不错的例子
并发容器
《Java程序员修炼之道》对ConcurrentHashMap、CopyOnWriteArrayList、BlockingQueue、LinkedBlockingQueue、ArrayLinkingQueue、TransferQueue[java1.7的新特性]都有不错的描述。
并发执行
- java开发中的ForkJoinPool、ForkJoinTask的分支合并框架,怎么都觉得像Map/Reduce。这个参考一下IBM的吧
推荐书籍:
- Java程序员修炼之道
- java并发编程