java线程传统VS现代【2】

来源:互联网 发布:单片机语言是什么语言 编辑:程序博客网 时间:2024/05/18 17:04
08、java5原子性操作类的应用
详细java.util.concurrent包及子包的API帮助文档。

09、java5线程池
    如果没有线程池,需要在run方法中不停判断,还有没有任务需要执行
    线程池的通俗比喻:接待客户,为每个客户都安排一个工作人员,接待完成后该工作人员就废掉。服务器每收到一个客户请求就为其分配一个线程提供服务,服务结束后销毁线程,不断创建、销毁线程,影响性能。
    线程池:先创建多个线程放在线程池中,当有任务需要执行时,从线程池中找一个空闲线程执行任务,任务完成后,并不销毁线程,而是返回线程池,等待新的任务安排。
    线程池编程中,任务是提交给整个线程池的,并不是提交给某个具体的线程,而是由线程池从中挑选一个空闲线程来运行任务。一个线程同时只能执行一个任务,可以同时向一个线程池提交多个任务。
线程池创建方法:
a、创建一个拥有固定线程数的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
b、创建一个缓存线程池 线程池中的线程数根据任务多少自动增删 动态变化
ExecutorService threadPool = Executors.newCacheThreadPool();
c、创建一个只有一个线程的线程池,与单线程一样,但好处是保证池子里有一个线程,当线程意外死亡,会自动产生一个替补线程,始终有一个线程存活。
ExecutorService threadPool = Executors.newSingleThreadExector();
d、往线程池中添加任务
threadPool.executor(Runnable)
e、关闭线程池:
threadPool.shutdown()线程全部空闲,没有任务就关闭线程池
threadPool.shutdownNow()  不管任务有没有做完,都关掉
f、用线程池启动定时器:
f-1、创建调度线程池,提交任务延迟指定时间后执行任务
Executors.newScheduledThreadPool(线程数).schedule(Runnable, 延迟时间,时间单位);
f-2、创建调度线程池,提交任务, 延迟指定时间执行任务后,间隔指定时间循环执行
Executors.newScheduledThreadPool(线程数).schedule(Runnable, 延迟时间,
间隔时间,时间单位);
f-3、所有的 schedule 方法都接受相对 延迟和周期作为参数,而不是绝对的时间或日期。将以 Date 所表示的绝对时间转换成要求的形式很容易。例如,要安排在某个以后的 Date 运行,可以使用:schedule(task, date.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS)。

10、Callable与Future的应用:获取一个线程的运行结果
public interface Callable<V>
返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。 Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。
只有一个方法V call() 计算结果,如果无法计算结果,则抛出一个Exception异常。
使用方法:
ExecutorService threadPool = Executors.newSingleThreadExccutor();
如果不需要返回结果,就用executor方法  调用submit方法返回一个Future对象
Future<T> future = threadPool.submit(new Callable<T>(){//接收一个Callable接口的实例对象
覆盖Callable接口中的call方法,抛出异常
public T call() throws Exception
{
ruturn T;
}
});
获取Future接收的结果
future.get();会抛出异常
future.get()没有拿到结果就会一直等待
Future取得的结果类型和Callable返回的结果类型必须一致,通过泛型实现。Callable要通过ExecutorService的submit方法提交,返回的Future对象可以取消任务。


public interface Future<V>
Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。 
方法摘要
 boolean cancel(boolean mayInterruptIfRunning)           试图取消对此任务的执行。
 V get()           如有必要,等待计算完成,然后获取其结果。
 V get(long timeout, TimeUnit unit)  如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。
 boolean isCancelled()           如果在任务正常完成前将其取消,则返回 true。
 boolean isDone()           如果任务已完成,则返回 true。

Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。
ExecutorService exec = Executors.newSingleThreadExecutor();
Future<String> future = 
exec.submit(new Callable<String>(){
@Override
public String call() throws Exception {
Thread.sleep(2000);
return "hello";
}

});
exec.shutdown();
System.out.println("等待结果");
try {
System.out.println("拿到结果: " + future.get());
} catch (Exception e) {
e.printStackTrace();
}


Callable要采用ExecutorSevice的submit方法提交,返回的future对象可以取消任务。
CompletionService用于提交一组Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。
好比我同时种了几块地的麦子,然后就等待收割。收割时,则是那块先成熟了,则先去收割哪块麦子
ExecutorService executor =  Executors.newFixedThreadPool(10);
CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(executor); 
for(int i=1;i<=10;i++){
final int seq = i;
completionService.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(new Random().nextInt(5000));
return seq;
}
});
}
executor.shutdown();

for(int i=1;i<=10;i++){
try {
System.out.println(completionService.take().get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}

}


11、Lock&Condition实现线程同步通信

Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
ReadWriteLock ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。

Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。注意lock.unlock();要放在finally语句块中。

读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!

读写Collection时使用读写锁
在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。 

通过Condition来进行线程通信。可以替代wait()和notify()方法。
在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。



0 0
原创粉丝点击