`
xiaoxiao_0311
  • 浏览: 22856 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

多线程的学习总结 较笼统

阅读更多
近段时间重温了java的并发,发现还蛮有意思的,现在总结一下大体的流程吧!
1、 为什么要并发
当计算机在处理应用的过程中,CPU、内存等并不是处于最佳状态(动力十足全力前进),这难免就会造成资源的浪费;在计算一个高精度高复杂性的科学计算时,就需要更快的处理速度,单线程有时会独木难支;在越来越丰富的界面化的应用中,用户的体验度就包括程序的响应速度,而单线程的程序显然难以达到要求。
2、共享对象的安全
在并发过程中,很多时候会用到线程间的通信,或者某个任务是由线程间的协作完成的,比如生产者/消费者问题,这时候,对于这种共享于两个或多个线程间的对象,就需要保证其“安全”。安全的定义其实很简单,那就是某个线程对共享对象的修改是可见的。在JAVA的定义中,为了提供更高的速度,是允许线程保存对象的复本的,只有在进入和离开同步代码块时,才会与原对象进行同步。
如何保证共享对象的安全呢,可以有以下方式:
  1> 共享对象的安全也即共享对象的状态属性安全,可以通过
      A> Volatile定义变量
      B> 使用automic原子变量类
      C> 将属性设为只读的final
      D> 在属性的更改方法中使用synchronized
      E> 在属性的更改方法中使用lock
注1:volatile定义的变量会直接从内存中读取并在修改后立即写回内存,禁止了java虚拟机对代码的优化,此类对象提供的读写均为原子操作,但自增操作等此类复合操作并非原子操作,不能保证该变量的安全。使用条件:新值不依赖之前的旧值,不依赖其它的状态变量,不需要放在synchronized等锁中。
注2:automic原子变量类在long,double等操作时有重要意义,对于64位的long,double类型数据写入时,会分别写入高位与低位,两次写入可能会导致安全问题。
注3:synchronized的锁其锁定的对象为

注4:Lock 其实现类ReentrantLock提供了可定时的可轮询的可中止的锁,但该锁是显示的,即需要手动加锁与解锁。
  2> 还可以通过其它方法实现,比如使用ThreadLocal,或者将对象用作线程独占的不作为共享对象使用,或者使用线程安全的Collection。
注:ThreadLocal为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突,使用put,get来存取。
3、线程的建立
  1> 创建线程的方式
      A> 实现Runnable接口,将执行代码块放入run方法中。
      B> 继承Thread类,重写run方法。
      C> 实现Callable接口,并实现call方法,该接口是用于有返回值的线程。
  2> 启动线程的方式
      A> 实现Runnable接口的类可以使用
            1. 以Runnable接口类的实例去实例化一个Thread对象,并调用该对象的start方法
            2. 实例化一个Executor的实现子类:
                  a) Executor.newCachedThreadPool  缓存0到无穷多个线程,用则建不用则毁
                  b) Executor.newFiledThreadPool  缓存min到max 个线程,自动管理个数
                  c) Executor.newScheduleddThreadPoolExecutor 可以定时执行,只支持相对时间
            执行executor.execute(Runnable)
      B> 继承Thread类的线程类,可以直接调用start方法启动线程
      C> 实现Callable接口的类需要使用Executor框架的子类,并调用submit方法来启动,在启动后会返回一个Future对象,该对象可以判定线程的状态:isDone,isCanneled,使用get方法取出线程的返回值。
4、线程的结束
  1> 设置结束状态位,在执行过程中不断监测该状态位的值,符合退出条件即退出。
  2> 使用异常结束,使用interrupt异常结束,这个异常在结束即将进入阻塞或已经阻塞的时候会立即执行,在正常运行时不会执行,但会对interrupted状态位置值,所以可以不断的监测该状态,并使用try-catch-finally块包围,在finally中释放相关资源。
5、线程的异常处理
异常是不会跨线程传递的。
  1> 在线程中或线程外捕获RuntimeException,加以处理
  2> Thread类可以设置setUncaughtExceptionHandler做异常处理
6、线程的协作
有时候,线程之间是相互配合操作的,比如生产者/消费者,所以就需要线程之间进行协作。线程之间的协作一般可以通过以下方式进行:
  1> 使用Object对象中的wait、notify、notifyAll进行
  2> 使用Condition对象中的await、signal、signalAll进行
  3> 使用线程的协作组件
7、线程的协作组件
线程中会有协作,比如生产者/消费者,以下是一些协作的组件
  1> CountDownLatch典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。在初始化时设置一个countDown值,该值一经设定不可改变,当后继任务需要前驱任务的结果时,需要await,当countdown的值为0时,则await的线程会被唤醒并执行。
  2> CyclicBarrier允许一组线程互相等待,直到到达某个公共屏障点。典型的应用场景是:是水运中的分段转运法,它使任务在完成某一阶段时进行await,当所有的任务均进入await状态时,即进入下一阶段的运行。
  3> DelayBlokingQueue 存放的是一组实现了Delay接口的对象,因为Delay继承了Comparable接口所以Delay的实现类必须重写compareTo方法,其中delay时间最长的也即最紧急的放在队列头,依次以delay时间排序。这是一个阻塞式的队列。
  4> PriorityBlockingQueue 是具有优先级的队列,其中的对象也必须实现Comparable接口,也是阻塞式的队列。
  5> Semaphore 更像是一个厕所,进入足够多的人后就不允许再进入了,每次进入都必须获取通告证acquire,当出来后则需要交还通告证release。
  6> Exchanger 提供两个线程间直接交换对象的方法,这样可以使用对象在线程为为独占的,当使用完成后调用exchange方法将当前任务挂起,等另一个任务也执行此方法时进入对象的交换,交换完成后双方任务自动继续执行。

哈哈,总结的有点太概括了,回头再弄分述下
  • 大小: 15.6 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics