java多线程笔记

线程基础

1、ThreadGroup 线程分组 可以将 许 多线程合并成一组进行管理。
2、ThreadLocal 线程局部变量,多个线程不需要共享数据的时候使用局部变量,默认实现initialValue的方法 ,可以通过set。get赋值。 
3、通过线程工厂进行创建线程,编写一个自定义类继承线程工厂,这样子做的好处是方便管理 多线程

线程同步

1程、同步机制是java用来解决多线共享数据问题的手段,表现形式有两种 :Lock接口及其实现机制 ,synchronized关键字机制。
2、如果一个对象被synchronized标识,那么只有一个执行线程被允许访问它,如果其他线程要访问的话,将被挂起,直到访问对象的线程执行完。
3、静态方法标识的synchronized,同一时间只允许一个执行线程访问,但是其他线程可以访问这个对象的非静态方法。
4、当一个线程访问到一个对象的同步方法的时候,它还可以调用这个对象的其他的同步方法,而不必再次去获取这个方法的访问权。
5、线程之间通信的方法,wait, notify  notifyAll。
6、Lock锁更加复杂。新方法tryLock(),试图获取锁,返回false表示获取失败可以继续执行下面的方法,而且获取lock 和释放lock可以不在一个代码块里面,更加灵活多变。
7、Lock接口允许读写分离,允许多个读线程和只有一个写线程。【实现类 ReentrantLock】
8、锁最大的改进是出现了ReadWriterLock接口和它唯一实现类ReentrantReadWriterLock。读锁加上了以后可以 多线程访问但是不可以修改,写锁加上以后 该线程是可以读也可以写的
9、修改锁的公平性,如果为初始化锁的时候默认是false,当多个线程同时等待锁的时候,锁将随机选择一个线程进入临界区, 如果为true当选择等待时间最长的进入临界区。
10、Lock锁的通信 使用Condition接口声明 目的是线程获取锁并且查看等待的某一个条件是否满足,如果不满足就挂起直到某个线程唤醒他们。await() signalAll()方法
     await(long time,TimeUnit unit) 方法   time时间过去以后就不等待,或者其他线程中断当前线程,或者调用当前线程的singal()或singalall()方法。
     awaitUninterruptibly() 该方法是不可中断的等待。只能调用singal或者singalall方法。
     awaitUnit(Date date) 基本同await(long time,TimeUnit unit)方法是一样的


线程同步辅助类

1、信号量(Semaphore):计数器,用来保护一个或者多个共享资源的访问,基础工具。
     CountDownLatch:在完成一组正在其他线程中执行操作之前,它允许线程一直等待。
     CyclicBarrier:它允许多个线程在某个集合点(common point)进行相互等待。
     Phaser:它把并发任务分成多个阶段运行,在开始下一个阶段之前,当前阶段中所有的线程都必须执行完成。java7新特性。
     Exchanger:它提供了两个线程之间的数据交换点。
2、在任何时候都可以使用Semaphore来保护临界区。
3、信号量的 作用原理, 当线程要访问一个共享资源的时候,必须先获取信号量,如果信号量内部的计数器大于0,获取以后信号量计数器-1,然后允许访问该资源,计数器大于0意味着该资源可以使用,如果计数器等于0,信号量会把线程休眠直至计数器大于0.计数器等于0的时候意味着所有的共享资源已经被其他线程使用了。 线程使用完毕以后 信号量释放,计数器+1。
       信号Semaphore的方法总结:  acquireUniterruptibly()获取信号量 忽略线程中断而且不会抛出任何异常。
                                                   tryAcquire()试图获取信号量,获得返回true,如果不能,就返回false。避开线程阻塞和等待信号量的释放。信号量 这个 也有公平性和非公平性的限制,默认都是非公平性的。
       acquire(),acquireUninterruptibly(),tryAcquire(),和release()方法都有另一种实现方式,即提供一个int型的传入参数。这个参数指的是线程试图获取或者释放的共享资源的数目。也就是线程想要在信号量内部计数器上删除和增加的数目。
4、CountDownLatch等待多个并发事件完成。就是一个同享数据必须等待N个线程同时等待的时候才允许访问。
      方法总结: await()需要等待其他事件先完成的线程调用。
                     countDown()方法 每个被等待的事件在完成的时候调用,当CountDownLatch的计数器变成0的时候,所有等待的线程将会被执行。
      CountDownLatch机制不是用来保护共享资源或者临界区的,它是用来同步执行多个任务的一个或者多个线程。
     CountDownLatch只准许进入一次,它的await(long time ,TimeUnit timeunit)同上。
5、在集合点同步CyclicBarrier。允许两个或者多个线程在某个点进行同步。与CountDownLatch类似,但更加强大。
     CyclicBarrier类的方法getNumberWaiting()方法和getParties()方法 前者返回阻塞的的线程的数目,后者返回被CyclicBarrier对象同步的任务数。与countDownLatch不同的是CyclicBarrier可以被重置回初始状态。重置的方法是reset()
6、并发阶段任务的运行 Phaser ,允许执行并发多阶段任务。它是在每一步结束的位置对线程进行同步。当所有线程完成了这一步,才允许完成下一步。必须对Phaser进行初始化,但是不同于其他同步机制,Phaser可以动态的增加或者减少任务数。
     
           Phaser类的方法详解:
                arrive():这个方法通知phaser对象一个参与者已经完成了当前阶段,但是它不会等待其他线程完成,也就是说这个方法不会与其他方法同步。
                awaitAdvance(int phase) 如果传入的阶段参数与当前阶段一致,这个方法会将当前线程休眠,直到这个阶段的所有参与者运行完成。如果传入的阶段参数与当前阶段不一致,这个方法立即返回。
                awaitAdvanceInterruptibly(int phase)这个方法跟awaitAdvance(int phase)一样,不同之处是,如果这个方法休眠中被打断,将抛出intter异常。
                将参与者注册到Phaser中
                register():这个方法将一个新的参与者注册到Phaser中,这个新的参与者将被当成没有执行本阶段的线程。
                bulkRegister(int Partes):这个方法将指定数目的参与者注册到Phaser中,所有这些新的参与者都将被当成没有执行完本阶段的线程。
                只提供一种方法减少注册者的数量:arriveAndDeregister().
                强制终止Phaser:当一个Phaser对象没有参与线程的时候,就处于终止状态。Phaser类提供了forceTermination()方法来强制Phaser进入终止状态,这个方法不管phaser中是否存在注册的参与线程,当一个参与线程产生错误的时候,强制phaser终止是很有意义的。当phaser处于终止态的时候,awaitAdvance()和arrivceAndAwaitAdvance()方法立即返回一个负数。而不再是一个正值了,如果知道phaser可能被终止,就需要验证这些方法的返回值,以确定phaser是不是被终止了。
7、并发阶段任务中的 阶段切换。Phaser类提供了onAdvance()方法,它在phaser阶段改变的时候会自动执行,onAdvance()方法需要两个int型的传入参数:当前阶段数以及注册的参与者数量,返回boolean值,false表示phaser在继续执行,返回true表示phaser已经完成执行并且进入了终止态。
      并发阶段任务阶段切换主要是用来每个并发结合点完成之后 要做的一些事情。通过继承 Phaser来实现。
8、并发阶段任务中的 数据交换
     java提供了一个同步辅助类Exchanger,它允许在并发任务之间交换数据。Exchanger类允许在两个线程之间定义同步点(Synchronization Point).当两个线程都到达同步点时,它们交换数据结构,因此第一个线程的数据结构进入第二个线程,,同时 第二个线程的数据结构进入到第一个线程。
       当线程A调用Exchange对象的exchange()方法后,他会陷入阻塞状态,直到线程B也调用了exchange()方法,然后以线程安全的方式交换数据,之后线程A和B继续运行

有5种同步辅助类适用于常见的同步场景

     1. Semaphore 信号量是一类经典的同步工具。信号量通常用来限制线程可以同时访问的(物理或逻辑)资源数量。
     2.CountDownLatch 一种非常简单、但很常用的同步辅助类。其作用是在完成一组正在其他线程中执行的操作之前,允许一个或多个线程一直阻塞。
     3.CyclicBarrier 一种可重置的多路同步点,在某些并发编程场景很有用。它允许一组线程互相等待,直到到达某个公共的屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier在释放等待线程后可以重用,所以称它为循环的barrier。
     4.Phaser 一种可重用的同步屏障,功能上类似于CyclicBarrier和CountDownLatch,但使用上更为灵活。非常适用于在 多线程环境下同步协调分阶段计算任务(Fork/Join框架中的子任务之间需同步时,优先使用Phaser)
     5.Exchanger 允许两个线程在某个汇合点交换对象,在某些管道设计时比较有用。Exchanger提供了一个同步点,在这个同步点,一对线程可以交换数据。每个线程通过exchange()方法的入口提供数据给他的伙伴线程,并接收他的伙伴线程提供的数据并返回。当两个线程通过Exchanger交换了对象,这个交换对于两个线程来说都是安全的。Exchanger可以认为是 SynchronousQueue 的双向形式,在运用到遗传算法和管道设计的应用中比较有用。

线程执行器

1、Executer线程池,分离了任务的创建和执行,执行器通过创建所需的线程,来负责这些对象的创建,实例化以及运行。当发送一个任务给执行器时,执行器会尝试使用线程池中的线程来执行这个任务,避免了不断的创建和销毁线程而导致系统性能下降。
2、执行器框架另一个重要的优势是 Callable接口。类似于Runable接口,但却提供了两方面的增强。
     a.接口的主方法名字为call(),可以返回结果。
     b.当发送一个Callable对象给执行器的时候,将获得一个实现了Future接口的对象。可以使用这个对象来控制Callable对象的状态和结果。
     ThreadPoolExecutor类 的两个方法:
           shutdownNow():立即关闭执行器,执行器不在执行那些正在等待执行的任务,这个方法返回等待执行的任务列表。调用时,正在运行的任务将继续执行。
          shutdown():表示不再往线程池里面添加线程。
           isTerminated():如果调用了shutdown()和shutdownNow()方法,并且执行器完成了关闭过程,返回true.
           isShutdown():如果调用了shutdown(),那么这个方法将返回true.
           getLargestPoolSize():返回曾近同时位于线程池中的最大线程数。
3、使用Executors线程工厂创建线程池的几个常用方法。
     a.newCachedThreadPool() : 创建一个根据自己线程的一个线程池,大小是有添加的线程而定的。
     b.newFixedThreadPool():创建一个固定大小的线程池。
     c.newSingleThreadExecutor()用来创建一个线程的线程池。
4、使用  Callable接口创建线程,用Future这个接口声明来获取Callable对象。管理状态isDone。Future.get()方法来获取线程的返回结果,这个方法一直等待直到Callable对象的call()方法执行完并完成返回结果。如果没有完成的话,这个方法一直处于阻塞状态。
5、 多个任务执行只返回第一个结果。主要是使用ThreadPoolExcutor类的invokeAny()方法接受一个任务列表,返回第一个完成并且没有抛出异常任务的执行结果。
6、多个任务执行并处理所有结果。主要是invokeAll()方法。
7、如果不想任务立即执行,而是过一段时间才执行 或者周期性执行 使用ScheduledThreadPoolExecutor类。
构造器里面的1表示线程池里面拥有的线程数。
定时执行  i+1表示要等待的时间,TimeUnit表示时间单位。
   任务将等待N秒。
      shutDown(): 可以通过配置ScheduledThreadPoolExecutor的setExecuteExitstingDelayedTasksAfterShutDownPolicy()方法来改变这个行为。传递false,执行shutdown方法,待处理的问题将不会被执行。
8、在执行器中周期性执行任务
     



9、执行器中取消任务 使用Future的cancel方法来进行控制。 cancel可以传递参数 true,false 。
          
     

10、 在执行器中控制任务的完成。FutureTask类提供了一个名为done()的方法, 允许执行器在执行任务结束之后,还可以执行一些代码。当任务执行完成是受FutureTask类控制。这个方法在内部被FutureTask类调用。在任务结果设置后以及任务的状态已改变为isDone之后,无论任务是否是正常结束还是被取消,done()方法都被调用。通过集成FutureTask来完成.
11、 在执行器中分离任务的启动与结果的处理.特殊情况一个对象里发送任务给执行器,然后在另一个对象里处理结果。对于这种情况。java并发API提供了CompletionService类。

     
12、 处理在执行器中被拒绝的任务。 当我们想结束执行器的执行时,调用shutdown()方法来表示执行器应当结束。但是执行器只有等待正在运行的任务或者等待执行的任务结束后,才能 真正结束。如何处理被执行器拒绝的任务。通过实现RejectExecutionHandler接口。

Fork/Join框架

1、java 1.7中包括了ExecutorService接口的另一种实现,用来解决特殊类型的问题。就是Fork/Join框架,也成 分解/合并框架。主要是解决一些能够通过分治技术将问题拆分成小任务的问题。分治技术主要是关键是 参考大小,可以使用任务中将要处理的元素的数目和任务执行所需要的时间来决定参考大小。
     测试不同的参考大小来决定解决问题最好的一个方案,将ForkJoinPool类看作一个特殊的Executor执行器类型。
     这个框架基于两种操作:
           分解Fork操作:当需要将一个任务拆分成更小的多个任务,在框架中执行这些任务。
           合并Join操作:当一个主任务等待其创建的多个子任务的完成执行。
      Fork/Join框架执行器框架主要的区别在于  工作窃取算法(Work-Stealing Algorithm)。与执行器框架不同,使用Join操作让一个主任务等待它所创建的子任务的完成,执行这个任务的线程称为  工作者线程, 工作者线程寻找其他扔未被执行的任务,然后开始执行。
      Fork/Join框架执行的任务有以下限制
          a、任务只能使用fork()和join()操作当做同步机制。如果使用其他同步机制,工作者线程就不能执行其他任务。
          b、任务不能执行I/O操作,比如文件数据的读写。
          c、任务不能抛出非运行时异常。必须在代码中处理掉。
       Fork/Join框架的核心是由下列两个类组成的
          a、ForkJoinPool:这个类实现了ExecutorService接口和工作窃取算法。它管理工作者线程,并提供任务的状态信息,以及任务的执行信息。
          b、ForkJoinTask:这个类是一个将在ForkJoinPool中执行的任务的基类。
       Fork/Join框架提供了一个任务里执行fork()和join()操作的机制和控制任务状态的方法。通常,为了实现fork/join任务,需要实现一个以下两个类之一的子类。
        Fork/join调用了invokeAll()方法来执行一个主任务所创建的子任务,是一个同步调用。这个任务将等待子任务的完成,然后才能继续执行。当一个主任务等待它的子任务时,执行这个主任务的工作者线程接受另一个等待执行的任务并开始执行,正因为有了这个行为,所以说Fork/Join框架提供了一种比Runable和Callable对象更加高效的任务管理机制
        Fork/Join框架的调用 execute方法如果传入的类没有实现task接口 而是Runable接口的话,不采用工作窃取算法。
        invoke(ForkJoinTask task) 是同步方法,这个方法直到传递进来的任务执行结束以后才返回。
2、合并任务的结果,一个任务被分成多个子任务,对于子任务返回的结果进行处理。
     
3、异步运行任务
     在ForkJoinPool中执行ForkJoinTask时,可以采用同步或异步的方式。当采用同步方法时,发送任务给Fork/Join线程池的方法直到任务执行完成后才会返回结果,而采用异步方法执行时,发送任务给执行器的方法将立即返回结果,但是任务仍能继续执行。
     

      ForkJoinTask类get与join方法总结:【同步方法】
          a.  get():返回compute()方法返回的结果。
          b.  get(long timeout,TimeUnit unit) : 如果任务的结果没有准备好,那么get()方法的这个版本将等待指定的时间。如果超过等待的时间,则返回null值。
          c.  get与join方法的 区别在于:
                    1.1 join方法不能被中断,如果中断调用Join()方法的线程方法要抛出InterrunptExcepion异常。
                    1.2 如果任务抛出任何运行时异常,那么get()方法将返回ExecutionException异常。但是join()方法返回的是RuntimeException异常。
4、在任务中抛出异常
     ForkJoinTask与ForkJoinPool类的异常信息与普通的不一样,在控制台上程序如果没有结束执行,不能看到任务的异常信息。可以通过一些方法来获知任务是否有异常抛出,以及抛出的是哪一种的异常。
5、取消任务
     在ForkJoinPool类中执行ForkJoinTask对象时,【借助于辅助类】在任务开始执行前可以取消它,ForkJoinTask类提供了cancel()方法来达到取消任务的目的,在取消一个任务时必须注意两点:
          a.ForkJoinPool类不提供任何方法来取消线程池中正在运行或者等待运行的所有任务。
          b.取消任务时,不能取消已经被执行的任务。

并发集合

 简介:
     java提供了一些可以用于并发程序中的数据集合,不会引起任何问题。一般分为两类:
          a.阻塞式集合(Blocking Collection) 这类集合包括添加和移除数据的方法,当集合已满或为空时,被调用的添加或者移除方法就不能立即执行,那么调用这个方法的线程将被阻塞,一直到该方法可以被成功执行。
          b.非阻塞式集合(Non-Blocking Collection) 这类集合也包括添加和移除数据的方法。如果方法不能立即执行的话,则返回null或者抛出异常,当是调用这个方法线程不会被阻塞。
     
1、 ConncurentLinkedDeque 非阻塞list
          方法:     
                     add方法向这个列表中插入元素到尾部。
                     poll方法获取列表中的元素队列中第一个,如果列表为空的话,这些方法返回null。
                     getFirst(), getLast()分别返回列表中第一个和最后一个元素,返回的元素不会从列表中移除。如果列表为空,方法抛出异常(NoSuchElementException)。
                     peekpeekFirst , peedLast 返回列表中的第一个和最后一个元素 返回的元素不会移除,如果列表为空,方法返回null。
                     remove, removeFirst, removeLast 分别返回列表中第一个和最后一个元素,返回的元素会从列表中移除,如果列表为空,抛出异常NoSuchElementException。
     

2、 LinkedBlockingDeque 阻塞式的列表
          方法:
                    put方法插入列表,如果列表已满,调用这个方法的线程将被阻塞知道列表中有了可用 的空间。
                    take方法取出字符串,如果列表为空,调用这个方法的线程将被阻塞直到列表不为空。
          
3、 使用按优先级排序的阻塞式线程安全列表 有序列表 PriorityBlockingQueue类来满足这类需求。
     PriorityBlockingQueue的元素必须实现Comparable接口,这个接口提供comparaeTo方法,他传入参数是一个同类型的对象,返回一个int类型的数。
     结构是  阻塞式数据结构(BlockingDataStructure).
4、使用带有延迟元素的线程安全列表 DelayQueue类。这个类可以存放带有激活日期的元素,当调用方法从队列中返回或提取元素时,未来的元素日期将被忽略,这些元素对于这写方法是不可见的。
      存放DelayQueue类中的元素必须继承Delayed接口。这个接口使对象成为延迟对象,并具有的激活日期,到了激活日期强制执行两个方法:
          compareTo(Delayed o )延迟值进行比较
          getDelay(TimeUnit unit) 该方法返回激活日期的剩余时间
     方法:
          clear : 移除所有的元素
          offer(E e) :E是DelayQueue的泛型参数,表示传入参数的类型,这个方法把参数对应的元素插入到队列中。
          peek():返回队列中第一个元素,但不移除。     
          take():返回队列中第一个元素,并将其移除,如果队列为空,线程将被阻塞。
5、使用线程安全可遍历映射
          ConcurrentNavigableMap接口以及实现类。
     


                
       
6、TaskLocalRandom类并发 随机生成数
7、原子变量,处理并发数据不一致的问题, 性能高于用同步机制保护的变量。
8、原子数组,当实现一个并发应用的时候,将不可避免的会有 多线程共享一个或者多个对象的现象,使用同步机制保护对这些共享属性的访问,有以下问题 :
     1、 死锁
     2、即使有一个线程访问共享对象,它扔需要 执行必须的代码获取和释放锁
     java中提供了更优雅的性能,java引入了比较和交换操作,这个操作使用以下三步修改变量的值:
           1、取得变量值,即变量的新值。
          2、在一个临时的变量中修改变量值,即新值。
          3、如果上面获取的变量旧值与当前变量值相等,就用新值替换旧值。如果已有其他线程修改了这个变量的值,上面获得的变量的旧值就可能与当前变量值不同。

定制并发类

1、 自定义接口并发工具:
          a、实现一个接口以拥有接口定义的功能。例如,ThreadFactory接口。
          b、覆盖类的一些方法,改变这些方法的行为。
2、定制ThreadPoolExecutor类
          Executor框架是一种将线程的创建和执行分离的机制。它基于Executor和ExecutorService接口,及这两个接口的实现类ThreadPoolExecutor展开。可传递的任务有如下两种:
          a、通过Runable接口实现的任务, 不返回结果。
          b、通过Callable接口实现的任务, 返回结果。
3、定制基于优先级的Executor类
          使用PriorityBlockingQueue队列,编写的定制类要实现compareable接口实现对比排序。
4、实现ThreadFactory接口生成的定制线程 工厂模式,工厂模式 主要是在生成一个自己的工厂类 编写自己的thread。
          
5、通过实现ThreadFactory接口为Fork/Join框架生成定制线程,Fork/Join框架是Executor和ExecutorSerivce接口的实现,这两个接口能够允许我们执行runble和calable任务,而不去关注这些任务的具体线程。这些执行器用于执行可以分拆成更小的任务体,主要组件如下 :
      a. 一种特殊类型的任务,由ForkJoinTask类来实现。
     b.两种操作,其中通过fork操作将一个任务拆分成多个子任务,而通过join操作等待子任务结束。
     c.工作窃取算法,用来对线程池的使用进行优化,当一个任务等待它的子任务时,执行这个任务的线程可以被用来执行其他任务。
     d.一个任务队列,存放的是等待被执行的任务。
     e.一个执行这些任务的线程池。

6、定制Lock锁
7、基于优先级别的传输队列【没看】
8、实现自己的原子对象

测试并发应用程序

1、测试是软件开发的基本环节,也称作 质量保证。(Quality Assurance) 典型的测试工具:Junit, Apache JMetter。
     并发应用程序实际上是有多个线程共享数据结构并进行交互,在测试并发应用程序时,要面对的最大问题是线程的执行是不确定的,无法保证线程的顺序,所以很难重现错误。
2、 监控Lock接口。Lock接口是Java并发API同步代码的基本机制之一,他定义了临界区。临界区是同一时间只能被一个线程执行的共享资源的代码块,这种机制是通过Lock接口和ReentrantLock类而实现的。
     ReentrantLock类的方法
          a. hasQueuedThreads(),返回的boolean值表明是否有线程正在等待获取锁。
          b. getQueueLength();返回正在等待获取锁的线程数。
          c. isLocked();返回的boolean值表示这个锁是否被一个线程占有。
          d. isFair();返回的boolean值表示该锁是否为公平模式。
          e. getHoldCount();返回当前线程获取到锁的次数。
          f. isHeldByCurrentThread();返回的boolean值表示当前线程是否正持有此锁。
3、监控 Phaser类 ,java并发API提供的最复杂最强大的功能之一是使用Phaser类执行并发阶段任务。当任务有并发任务被分为几步时这一机制非常有用。Phaser类提供了在每步结束时间时同步等待其他线程的机制。
4、监控执行器框架,Executor Framework 提供了一套机制把任务的实现和创建与管理执行这些任务的线程分开,如果使用一个执行器,必须实现Runable对象并将其发送到执行器。执行器负责管理线程。
5、监控 Fork/Join池。
     
     
      
6、输出高效的日志信息,日志系统是将信息输出到一个或者多个目标上的一种机制。
          LogSystem日志系统包含三个组件:
               a.一个或者多个处理器Handler:决定日志的格式,可以把日志消息输出到控制台上,写到文件中或保存到数据库中。
               b.一个名称Name
               c.一个级别level:输出级别。
          使用日志主要是用来  尽可能多的输出错误信息,输出错误的类和方法。

7、使用FindBugs分析并发代码....
8、使用Eclipse调试并发代码
      测试出现问题 无论我设置调式模式 Suspend VM 与Suspend Thread 没有什么不同的表现   全部在断点处停止 ????
     网上资料没有尚未找到结果。有可能是Eclipse版本的问题 ,但是这种问题比较少。

9、配置NetBeans调试并发代码 这个是另一种IDE工具  没有时间看 。。。
10、MultithreadedTC测试并发代码
     MutithreadedTC是一个测试并发应用程序的java类库, 主要目的是解决不确定的并发应用程序存在的问题。MultithreadedTC用一个内部节拍器来控制应用程序不同线程的顺序。
      


src.zip
100.5 KB






你可能感兴趣的:(java多线程笔记)