您的位置:首页 > 移动开发 > Android开发

Java/Android多线程并发、同步,线程之间通信,主、子线程的一些问题(CountDownLatch、CyclicBarrier和Semaphore)

2016-07-20 12:49 666 查看
  主线程和子线程?

40个Java多线程问题总结- http://geek.csdn.net/news/detail/197712?ref=myread
Android并发编程- http://blog.csdn.net/nugongahou110/article/category/5989691
进程与线程:

 进程是对运行时程序的封装,可以保存程序的运行状态,实现操作系统的并发;

 线程是进程的子任务,保证程序的实时性;

 进程是操作系统资源的分配单位,线程是CPU调度的基本单位;

 进程让操作系统的并发性成为可能,而线程让进程的内部并发成为可能。

Java并发及多线程并发- http://blog.csdn.net/wangyangzhizhou/article/category/2523519
Java并发编程的艺术(八)——闭锁、同步屏障、信号量详解- http://blog.csdn.net/u010425776/article/details/54580082
> Java并发

-wait()方法会释放CPU执行权 和 占有的锁。

-sleep(long)方法仅释放CPU使用权,锁仍然占用;线程被放入超时等待队列,与yield相比,它会使线程较长时间得不到运行。

-yield()方法仅释放CPU执行权,锁仍然占用,线程会被放入就绪队列,会在短时间内再次执行。

-wait和notify必须配套使用,即必须使用同一把锁调用;

-wait和notify必须放在一个同步块中

-调用wait和notify的对象必须是他们所处同步块的锁对象。

关于Java并发编程的总结和思考- http://blog.csdn.net/qq_35101189/article/details/54880625
> 深入理解线程

Android线程管理(一)——线程通信- http://www.cnblogs.com/younghao/p/5116819.html
  (Handler message Looper msg msgQueue)

Android线程管理(二)——ActivityThread- http://www.cnblogs.com/younghao/p/5126408.html
Android线程管理(三)——Thread类的内部原理、休眠及唤醒-http://www.cnblogs.com/younghao/p/5141295.html

  线程通信、同步、协作是多线程编程中常见的问题。线程协作通常是采用线程休眠及唤醒来实现的,线程的休眠通过等待某个对象的锁(monitor)实现(wait()方法),当其他线程调用该对象的notify()方法时,该线程就被唤醒。该对象实现在线程间数据传递,多个线程通过该对象实现协作。

  线程协作的经典例子是Java设计模式中的“生产者-消费者模式”,生产者不断往缓冲区写入数据,消费者从缓冲区中取出数据进行消费。在实现上,生产者与消费者分别继承Thread,缓冲区采用优先级队列PriorityQueue来模拟。生产者将数据放入缓冲区的前提是缓冲区有剩余空间,消费者从缓冲区中取出数据的前提是缓冲区中有数据,因此,这就涉及到生成者线程与消费者线程之间的协作。

> Android多线程一(AsyncTask实现原理)-http://blog.csdn.net/pipisky2006/article/details/8270222

AsyncTask的本质是一个线程池 BlockingQueue<Runnable> LinkedBlockingQueue<Runnable>(10) 

总结:

1、 AsyncTask的本质是一个静态的线程池,AsyncTask派生出的子类可以实现不同的异步任务,这些任务都是提交到静态的线程池中执行。

2、线程池中的工作线程执行doInBackground(mParams)方法执行异步任务

3、当任务状态改变之后,工作线程会向UI线程发送消息,AsyncTask内部的InternalHandler响应这些消息,并调用相关的回调函数

4,AsyncTask在Activity因为OnConfiguration重绘时要注意。

-----------------------------------------------------

【Java】并发之锁与条件-- http://blog.csdn.net/rongxinhua/article/details/19245755
【Java】并发之阻塞队列-- http://blog.csdn.net/rongxinhua/article/details/19507937
如何正确地停止一个线程?-- http://www.cnblogs.com/greta/p/5624839.html
> Thread类也是Runnable接口的子类;Thread运行在父类的run方法中,Runnable运行在实现Runnable接口的子类对象run方法中。

  Java 多线程实现接口Runnable和继承Thread区别-- http://blog.sina.com.cn/s/blog_9cbb6a210102ux44.html
  java线程系列---Runnable和Thread的区别、线程同步-- http://blog.csdn.net/guoxiaolongonly/article/details/50717574
> 多线程之间交换数据靠内存来完成。

同步机制关键字 synchronized

显示锁 ReentrantLock与Condition

信号量 Semaphore

循环栅栏 CyclicBarrier

闭锁 CountDownLatch

CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现.

  CyclicBarrier字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。

  Semaphore翻译成字面意思为 信号量,Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

  Exchanger可以在两个线程之间交换数据,只能是2个线程,他不支持更多的线程之间互换数据。

  volatile:Java 语言提供了一种稍弱的同步机制,即 volatile 变量.用来确保将变量的更新操作通知到其他线程,保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新. 当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的.

  volatile修饰的变量可以实现基本的加载和赋值的原子性.在JDK1.5之前我们只能通过 synchronized(阻塞的方式)实现这些复合操作的原子性,在JDK1.5中java.util.concurrent.atomic 包提供了若干个类能实现对int,long,boolean,reference的几个特殊方法非阻塞原子性

> 同步锁 /并发锁/ 读写锁 /原子性/、  Exchanger 、volatile、 future/futureTask、 atomics 等

> Java主线程等待所有子线程执行完毕再执行解决办法集: http://blog.csdn.net/fengshizty/article/details/41356845?utm_source=tuicool&utm_medium=referral
> Java如何等待子线程执行结束: http://www.cnblogs.com/langtianya/archive/2012/11/29/2794695.html
> 浅析Java中CountDownLatch用法:http://navylee.iteye.com/blog/1601648

> Java并发编程:CountDownLatch、CyclicBarrier和Semaphore:http://www.cnblogs.com/dolphin0520/p/3920397.html

> CyclicBarrier介绍:http://www.cnblogs.com/whgw/archive/2011/09/29/2195669.html

> Java并发之CountDownLatch、CyclicBarrier和Semaphore:http://developer.51cto.com/art/201403/432095.htm 
> Java 并发专题 : Semaphore 实现 互斥 与 连接池   http://blog.csdn.net/lmj623565791/article/details/26810813
> 线程池

  - Thread.join()在AsyncTask中的妙用:http://melord.iteye.com/blog/1555551

  - Java四种线程池的使用:http://blog.csdn.net/u013216593/article/details/42426289

  - 四种线程池:newCacheThreadPool newFixedThreadPool newScheduledThreadPool newSingleThreadExecutor

> 线程之间通信:join/wait/notify/notifyAll方法,主、子线程之间通信:Handler(Android)等

> CountDownLatch的使用

CountDownLatch latch = new CountDownLatch(5)  //声明计数器为5个

Thread t = new Thread() {

                    public void run() {

  try {

                                      //TODO 你的应用

                                } catch (Exception e) {

                                      //TODO 异常处理

                                }

                                finally {

       latch.countDown();  //这句是关键

                                     System.out.println("ok");  //5个线程都跑完后输出

  }

                        }

         };

 t.start();

然后让以上操作循环五次(就是说同时开5个线程),那么这个"ok"就会在等到这5个线程都ok后才会被输出一次。

> A线程执行完后,B线程再执行: 用监听这模式试一下:A线程处理完了,发送事件,B线程接受事件在处理,这样就不用耦合了,不知道这个是不是你的需求

-------------------------------------------------------------------------------------------------------------------------------

> Android java 中如何优雅的结束线程

线程对象属于一次性消耗品,一般线程执行完run方法之后,线程就正常结束了,线程结束之后就报废了,不能再次start,只能新建一个线程对象。但有时run方法是永远不会结束的。例如在程序中使用线程进行Socket监听请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。当需要结束线程时,如何退出线程呢?

    有三种方法可以结束线程:

  1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止

  2. 使用interrupt()方法中断线程

  3. 使用stop方法强行终止线程(不推荐使用,可能发生不可预料的结果)

前两种方法都可以实现线程的正常退出,也就是要谈的优雅结束线程;第3种方法相当于电脑断电关机一样,是不安全的方法。

    1)使用退出标志终止线程

  使用一个变量来控制循环,例如最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出。代码如下:

public class ThreadSafe extends Thread {

    public volatile boolean exit = false; 

        public void run() { 

        while (!exit){

            //do something

        }

    } 

}

定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false.在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值,

  2)使用interrupt()方法终止线程

  使用interrupt()方法来终端线程可分为两种情况:

  线程处于阻塞状态,如使用了sleep,同步锁的wait,socket的receiver,accept等方法时,会使线程处于阻塞状态。当调用线程的interrupt()方法时,系统会抛出一个InterruptedException异常,代码中通过捕获异常,然后break跳出循环状态,使线程正常结束。通常很多人认为只要调用interrupt方法线程就会结束,实际上是错的,一定要先捕获InterruptedException异常之后通过break来跳出循环,才能正常结束run方法。

public class ThreadSafe extends Thread {

    public void run() { 

        while (true){

            try{

                    Thread.sleep(5*1000);阻塞5妙

                }catch(InterruptedException e){

                    e.printStackTrace();

                    break;//捕获到异常之后,执行break跳出循环。

                }

        }

    } 

}

  线程未进入阻塞状态,使用isInterrupted()判断线程的中断标志来退出循环,当使用interrupt()方法时,中断标志就会置true,和使用自定义的标志来控制循环是一样的道理。

public class ThreadSafe extends Thread {

    public void run() { 

        while (!isInterrupted()){

            //do something, but no tthrow InterruptedException

        }

    } 

}

  为什么要区分进入阻塞状态和和非阻塞状态两种情况了,是因为当阻塞状态时,如果有interrupt()发生,系统除了会抛出InterruptedException异常外,还会调用interrupted()函数,调用时能获取到中断状态是true的状态,调用完之后会复位中断状态为false,所以异常抛出之后通过isInterrupted()是获取不到中断状态是true的状态,从而不能退出循环,因此在线程未进入阻塞的代码段时是可以通过isInterrupted()来判断中断是否发生来控制循环,在进入阻塞状态后要通过捕获异常来退出循环。因此使用interrupt()来退出线程的最好的方式应该是两种情况都要考虑:

public class ThreadSafe extends Thread {

    public void run() { 

        while (!isInterrupted()){ //非阻塞过程中通过判断中断标志来退出

            try{

                Thread.sleep(5*1000);//阻塞过程捕获中断异常来退出

            }catch(InterruptedException e){

                e.printStackTrace();

                break;//捕获到异常之后,执行break跳出循环。

            }

        }

    } 

}

  3)使用stop方法终止线程

  程序中可以直接使用thread.stop()来强行终止线程,但是stop方法是很危险的,就象突然关闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,不安全主要是:thread.stop()调用之后,创建子线程的线程就会抛出ThreadDeatherror的错误,并且会释放子线程所持有的所有锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放(不可控制),那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。因此,并不推荐使用stop方法来终止线程。

------------------------------------------------------------------------------------------------------------

> Thread与AsyncTask的区别:

(1)创建下载子线程:

  /**

       * 下载线程

       */

      private Thread myThread =new Thread(new Runnable() {

          @Override

          public void run() {

              Object data=download(PATH);

              Message msg=Message.obtain();

              msg.what=101;

             msg.obj=data;

             handler.sendMessage(msg);

         }

     });

(2)在Handler中执行下载之后的任务:

  private Handler handler=new Handler(new Handler.Callback() {

          @Override

          public boolean handleMessage(Message msg) {

              if(msg.what==101){

                  data=msg.obj;

                  //下面执行对data的操作

              }

              return false;

         }

     });

  Android 原生的 AsyncTask.java 是对线程池的一个封装,使用其自定义的 Executor 来调度线程的执行方式(并发还是串行),并使用 Handler 来完成子线程和主线程数据的共享。

  尽管Java中的多线程是个很好的机制,但是在使用时要注意它的副作用,学会使用对他进行封装之后的类和方法。

  > 如果我们创建大量的(特别是在短时间内,持续的创建生命周期较长的线程)野生线程,往往会出现如下两方面的问题:

  1.每个线程的创建与销毁(特别是创建)的资源开销是非常大的;

  2.大量的子线程会分享主线程的系统资源,从而会使主线程因资源受限而导致应用性能降低。

Future<Object> future = executor.submit(harReturnValuetask);

try {

     Object s = future.get();

} catch (InterruptedException e) {

    // 处理中断异常

} catch (ExecutionException e) {

    // 处理无法执行任务异常

} finally {

    // 关闭线程池

    executor.shutdown();

}

    > 销毁activity时注意关闭线程

  在Activity开启的子线程并不会自动随Activity的destroy而关闭,所以必须手动去关闭子线程或者通过boolean的方式让子线程结束运行。开启的子线程有for循环的要更加注意。

 boolean stopThread=false;

 protected void onDestroy() {

         System.out.println("-----------onDestroy------");

         stopThread=true;

         super.onDestroy();

     };

    耗时不多且较简单的任务可以用AsyncTask,耗时长的适合自己用线程池,因为管理更方便。AsyncTask只是Android提供的一个封装好的实现多线程的简单工具,但是管理起来并不方便,另外也有不少功能更好的第三方库来替代它

  Android消息处理机制:源码剖析Handler、Looper,并实现图片异步加载: http://blog.csdn.net/u012403246/article/details/45949963
  Java 8无人谈及的八大功能: http://www.cnblogs.com/lianghaoc/articles/5699871.html
----------------------------------------------------

> 同步,异步,串行与并行:

Java同步机制有4种实现方式:

① ThreadLocal ② synchronized( ) ③ wait() 与 notify() ④ volatile . 同步方法或者同步块中

异步的同义语是非阻塞(None Blocking)。

 以通讯为例 

   同步:发送一个请求,等待返回,然后再发送下一个请求 

   异步:发送一个请求,不等待返回,随时可以再发送下一个请求 

   并发:同时发送多个请求

串行与并行:

  有两种执行程序的方法。一种是顺序执行,另一程是并发执行。所谓顺序执行就是指程序中的程序段必须按照先后顺序来执行,也就是只有前面的程序段执行完了,后面的程序段才能执行。这种做法极大地浪费了CPU资源,比如系统中有一个程序在等待I/O输入,那么CPU除了等待就不能做任何事情了。为了提高CPU的使用效率、支持多任务操作,操作系统中引入了并发技术。所谓并发是指系统中的多个程序或程序段能够同时执行,这里的同时执行并不是指某一个时刻多段程序在同进执行(除非有多个CPU),而是CPU能把时间分给不同的程序段。比如前面等待I/O的例子,若采用并发技术,当一个程序在等待I/O时,系统可以把CPU资源分配给另外的程序,这样能减少CPU的空闲时间提高了资源利用率。

Android中多线程的使用四种方式最全总结- https://mp.weixin.qq.com/s?__biz=MzI0MjE3OTYwMg==&mid=2649549195&idx=1&sn=41a1ac9f44e6a6550778e953cbc6f1fe&chksm=f11802f6c66f8be02f7ef79bfd902775e32a4ed8ec2f68365919a691d2a34e0dcaa41ddaf8b3#rd?ref=myread
Java +安卓 定时任务- http://blog.csdn.net/forezp/article/details/52057118?ref=myread
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: