黑马程序员---张老师高新技术之多线程及java5中多线程新技术

来源:互联网 发布:富士康交友软件 编辑:程序博客网 时间:2024/05/16 10:42

------- android培训java培训、期待与您交流! ----------

传统线程机制的回顾
创建线程的两种传统方式
1-在Thread子类覆盖的run方法中编写运行代码,即继承Thread类
涉及一个以往知识点:能否在run方法声明上抛出InterruptedException异常,以便省略run方法内部对Thread.sleep()语句的try…catch处理?
2-在传递给Thread对象的Runnable对象的run方法中编写代码,即实现Runnable接口
总结:
1--查看Thread类的run()方法的源代码,可以看到其实这两种方式都是在调用Thread对象的run方法,如果Thread类的run方法没有被覆盖,
并且为该Thread对象设置了一个Runnable对象,该run方法会调用Runnable对象的run方法。
2--如果在Thread子类覆盖的run方法中编写了运行代码,也为Thread子类对象传递了一个Runnable对象,那么,线程运行时的执行代码是子类的run方法的代码.
涉及到的一个以往知识点:匿名内部类对象的构造方法如何调用父类的非默认构造方法。
3--多线程不会提高程序的运行效率,只是提高了处理多个任务的效率


线程的同步互斥与通信
非静态方法上的synchronized锁是this锁,也就是当前对象.
静态方法上的synchronized锁是所在对象的字节码锁.
注意:要用到共同数据(包括同步锁)或共同算法的若干个方法应该归在同一个类身上,这种设计正好体现了高类聚和程序的健壮性.
wait()方法一定要放在while循环判断里面,防止伪唤醒,提高程序的健壮性.
wait和notify必须放在synchronized中.
多个线程访问共享对象和数据的方式
如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。
如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:
将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。
上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。
总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。
极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。


线程范围内共享数据(也就是一个线程添加的数据和这个线程提取的数据是同一个,和其他线程操作的对象独立开)
关于线程范围内的变量共享的举例:创建三个线程,它们都访问了三个对象,第一个对象设置值,第二三个对象取值,同一个线程设置的值,只能被相同的线程获取。
解决方法,创建一个map,把线程作为key,线程操作的数据作为value,
这样的话,每个线程锁操作的数据就是不同的,相互独立开了
ThreadLocal实现线程范围的共享变量
ThreadLocal就相当于一个map,有set,get方法,set的值就是与当前线程相关的,get方法也是取与当前线程相关的ThreadLocal对象的值.
每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录,key分别是各自的线程,value是各自的set方法传进去的值。
在线程结束时可以调用ThreadLocal.clear()方法,这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量。
实现对ThreadLocal变量的封装,让外界不要直接操作ThreadLocal变量。
对基本类型的数据的封装,这种应用相对很少见。
对对象类型的数据的封装,比较常见,即让某个类针对不同线程分别创建一个独立的实例对象。
使用时直接用MyData.getInstance().setName();
class MyData{
private String name;(有对应的set和get方法)
private int age;(有对应的set和get方法)
private MyData(){}
private static ThreadLocal<MyData> myLocal = new ThreadLocal<MyData>();
public synchronized static MyData getInstance(){
MyData instance = myLocal.get();
if(instance == null){
instance = new MyData();
myLocal.set(instance);
}
return instance;
}
}
   注意:一个ThreadLocal代表一个变量,故其中里只能放一个数据,你有两个变量都要线程范围内共享,则要定义两个ThreadLocal对象。
如果有一个百个变量要线程共享呢?那请先定义一个对象来装这一百个变量,然后在ThreadLocal中存储这一个对象。

java5中的线程并发库
java.util.concurrent
在并发编程中很常用的实用工具类。此包包括了几个小的、已标准化的可扩展框架,以及一些提供有用功能的类,没有这些类,这些功能会很难实现或实现起来冗长乏味。
java.util.concurrent.atomic
类的小工具包,支持在单个变量上解除锁的线程安全编程。
可以对基本数据,对数组中的基本数据,对类中的基本数据等进行操作
   线程池:
如果访问服务器的客户端很多,那么服务器要不断地创建和销毁线程,这将严重影响服务器的性能。如果真的来一名学员,我们都安排一名新工作人员为之服务
,也是不可能的,那公司岂不是要招聘很多工作人员?而是应该一名工作人员服务完一名学员,空闲下来后,一旦有新的学员要服务,我又立即安排该工作人员为新学员服务。
     ***线程池的概念与此类似,首先创建一些线程,它们的集合称为线程池,当服务器接受到一个客户请求后,就从线程池中取出一个空闲的线程为之服务,服务完后不关闭该线程,而是将该线程还回到线程池中。
     
     Executors类及其应用
创建固定大小的线程池
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
ExecutorService.newFixedThreadPool(线程数);
创建缓存线程池
ExecutorService.newCachedThreadPool(); 
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
创建单一线程池
Executors.newSingleThreadExecutor();
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
关闭线程池
shutdown与shutdownNow的比较:shutdown是所有线程都空闲后关闭线程池,shutdownNow是现在就关闭线程池,不管池中还有没有运行的线程
     用线程池启动定时器
调用ScheduledExecutorService的schedule方法,返回的ScheduleFuture对象可以取消任务。
Executors.newScheduledThreadPool(线程数).schedule(运行的线程,时间, 时间单位);
支持间隔重复任务的定时方式,不直接支持绝对定时方式,需要转换成相对时间方式
     Callable&Future
Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。
Callable要采用ExecutorSevice的submit方法提交,返回的future对象可以取消任务。
CompletionService用于提交一组Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。
好比我同时种了几块地的麦子,然后就等待收割。收割时,则是哪块先成熟了,则先去收割哪块麦子。

     Lock&Condition实现线程同步通信
1-Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。
2-读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。
 如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!
3-在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,
 并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。
4-一个锁内部可以有多个Condition,即有多路等待和通知,可以参看jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例,从中除了要体味算法,还要体味面向对象的封装。
 在传统的线程机制中一个监视器对象上只能有一路等待和通知,要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。
 (如果只用一个Condition,两个放的都在等,一旦一个放的进去了,那么它通知可能会导致另一个放接着往下走。)


     Semaphore实现信号灯
一个计数信号量。从概念上讲,信号量维护了一个许可集。
Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
等待对象可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。
单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。
acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
release():释放一个许可,将其返回给信号量。
availablePermits():返回此信号量中当前可用的许可数。
     其他同步工具类
CyclicBarrier
表示大家彼此等待,大家集合好后才开始出发,分散活动后又在指定地点集合碰面,这就好比整个公司的人员利用周末时间集体郊游一样,
先各自从家出发到公司集合后,再同时出发到公园游玩,在指定地点集合后再同时开始就餐.
总结:也就是在每个线程中都await();等所有线程都执行到await()后,所有线程都继续向下执行.
CountDownLatch
犹如倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减1,当计数到达0时,则所有等待者或单个等待者开始执行。
这直接通过代码来说明CountDownLatch的作用,这样学员的理解效果更直接。可以实现一个人(也可以是多个人)等待其他所有人都来通知他,
可以实现一个人通知多个人的效果,类似裁判一声口令,运动员同时开始奔跑,或者所有运动员都跑到终点后裁判才可以公布结果,用这个功能做百米赛跑的游戏程序不错哦!
还可以实现一个计划需要多个领导都签字后才能继续向下实施的情况。
总结:多个线程都等在await()位置,当该CountDownLatch对象的设定值countDown()到0时,所有线程继续
Exchanger
用于实现两个线程之间的数据交换,每个线程在完成一定的事务后想与对方交换数据,第一个先拿出数据的线程将一直等待第二个线程拿着数据到来时,才能彼此交换数据。
exchang():等待另一个线程到达此交换点(除非当前线程被中断),然后将给定的对象传送给该线程,并接收该线程的对象。
可阻塞队列
一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。
队列的头部是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。
ArrayBlockingQueue
只有put方法和take方法才具有阻塞功能
可用两个具有1个空间的队列来实现同步通知的功能。
其他可阻塞队列
LinkedBlockingQueue、ArrayBlockingQueue、SynchronousQueue、PriorityBlockingQueue 和 DelayQueue。
同步集合
传统方式下的Collection在迭代集合时,不允许对集合进行修改。
ava5中提供了如下一些同步集合类:
ConcurrentHashMap-----相当于同步的HashMap,性能更优
ConcurrentSkipListMap-----相当于同步的 TreeMap,性能更优
CopyOnWriteArrayList-----相当于同步的ArrayList,性能更优
CopyOnWriteArraySet-----相当于同步的set,性能更优
总结:并发修改集合时如果不是同步集合,容易报错乃至死循环.
所以在多线程操作集合的时候一定要小心同步问题


线程问题总结:

在使用多线程的时候,一定要考虑清楚几个问题:

1.线程是否在操作共享数据

2.线程内是否有共享数据

3.线程以什么样的形式运行(也就是线程内代码运行的条件)

特别要注意操作数据的同步问题,选择合适的工具进行操作.


------- android培训java培训、期待与您交流! ----------

原创粉丝点击