1、并发和并行:
并发是同一时间应对(dealing with)多件事情的能力;
并行是同一时间作(doing)多件事情的能力。程序员
2、并行架构:
位级并行,32位计算机的运行速度比8位计算机更快,由于并行,对于32位数的加法,8位计算机必须进行屡次8位计算,而32位计算机能够一步完成,即并行的处理32位的4字节。
指令级(instruction-level)并行,程序员一般能够不关心处理器内部并行的细节,由于尽管处理器内部的并行度很高,可是通过精心设计,从外部看上去全部处理都像是串行的。
数据级(data)并行,数据级并行(也称为“单指令多数据”,SIMD)架构,能够并行地在大量数据上施加同一操做。这并不适合解决全部问题,但在适合的场景却能够大展身手。
任务级(task-level)并行,终于来到了你们所认为的并行形式——多处理器。从程序员的角度来看,多处理器架构最明 显的分类特征是其内存模型(共享内存模型或分布式内存模型)。编程
对于共享内存的多处理器系统,每一个处理器都能访问整个内存,处理器之间的通讯主要经过内存进行。
对于分布式内存的多处理器系统,每一个处理器都有本身的内存,处理器之间的通讯主要经过网络进行。
用并发的目的,不只仅是为了让程序并行运行从而发挥多核的优点。若正确使用并发,程序还将得到如下优势:及时响应、高效、容错、简单。安全
注意:不该该在产品代码上,使用Thread类等底层服务。网络
3、七个模型多线程
一、线程与锁:线程与锁模型有不少众所周知的不足,但还是其余模型的技术基础,也是不少并 发软件开发的首选。 二、函数式编程:函数式编程日渐重要的缘由之一,是其对并发编程和并行编程提供了良好的支 持。函数式编程消除了可变状态,因此从根本上是线程安全的,并且易于并行执行。 三、Clojure之道——分离标识与状态:编程语言Clojure是一种指令式编程和函数式编程的混搭方 案,在两种编程方式上取得了微妙的平衡来发挥二者的优点。 四、actor:actor模型是一种适用性很广的并发编程模型,适用于共享内存模型和分布式内存模型, 也适合解决地理分布型问题,能提供强大的容错性。 五、通讯顺序进程(Communicating Sequential Processes,CSP):表面上看,CSP模型与actor模 型很类似,二者都基于消息传递。不过CSP模型侧重于传递信息的通道,而actor模型侧重于通道 两端的实体,使用CSP模型的代码会带有明显不一样的风格。 六、数据级并行:每一个笔记本电脑里都藏着一台超级计算机——GPU。GPU利用了数据级并行, 不只能够快速进行图像处理,也能够用于更广阔的领域。若是要进行有限元分析、流体力学计算 或其余的大量数字计算,GPU的性能将是不二选择。 七、Lambda架构:大数据时代的到来离不开并行——如今咱们只须要增长计算资源,就能具备 处理TB级数据的能力。Lambda架构综合了MapReduce和流式处理的特色,是一种能够处理多种大数据问题的架构。
4、线程与锁:架构
class Counter { private int count = 0; public synchronized void increment() { ++count; } public int getCount() { return count; } } 毋庸置疑,对于增长了同步功能的代码,每次执行都将获得正确结果,但代码中仍隐藏了一个bug。 潜藏的bug是: 除了increment()以外,getCount()方法 也须要进行同步。 不然,当一个线程对值的修改没有及时更新到主内存,从而致使 调用getCount()的线程可能得到一个失效的值。 解释: Java内存模型定义了什么时候一个线程对内存的修改对另外一个线程可见。 基本原则是,若是读 线程和写线程不进行同步,就不能保证可见性。 然而两个线程都须要进行同步。只在其中一个线程进行同步是不够的, 竞态条件: 计算的正确性取决于多个线程的交替执行时序时,就会发生竞态条件。 一、乱序执行。执行依赖于检测的结果,而检测结果依赖于多个线程的执行时序。 乱序缘由: 编译器的静态优化能够打乱代码的执行顺序; JVM的动态优化也会打乱代码的执行顺序; 硬件能够经过乱序执行来优化其性能。
因此在多线程环境下,对一个文件的操做须要加锁。并发
二、延迟初始化: 线程A和线程B同时执行getInstance,可能会取到两个实例对象,主要看线程执行时序了。 public class ObjFactory { private Obj instance; public Obj getInstance(){ if(instance == null){ instance = new Obj(); } return instance; } }
5、来自外星方法的危害编程语言
规模较大的程序经常使用监听器模式(listener)来解耦模块。 在这里,咱们构造一个类从一个URL 进行下载,并用ProgressListeners监听下载的进度。 public class Downloader extends Thread { private InputStream in; private OutputStream out; private ArrayList<ProgressListener> listeners; public Downloader(URL url,String outputFilename) throws IOException { in=url.openConnection().getInputStream(); out = new FileOutputStream(outputFilename); listeners=new ArrayList<ProgressListener>(); } public synchronized void addListener(ProgressListener listener){ listeners.add(listener); } public synchronized boolean remove(ProgressListener listener){ return listeners.remove(listener); } /*** * 来自外星方法的危害 * * addListener()、removeListener()和updateProgress()都是同步方法, * 多线程能够安全地使用这些方法。尽管这段代码仅使用了一把锁,但仍隐藏着一个死锁陷阱。 * * 陷阱在于updateProgress()调用了一个外星方法——但对于这个外星方法一无所知。外星方法能够作任何事情, * 例如持有另一把锁。这样一来,咱们就在对加锁顺序一无所知的状况下使用了两把锁。就像前面提到的,这就有可能发生死锁。 * * @param n */ private synchronized void updateProgress(int n){ for(ProgressListener listener:listeners){ listener.onProgress(n); } } /*** * 一种方法是在遍历以前对listeners进行保 护性复制(defensive copy), * 再针对这份副本进行遍历 * 这是个一石多鸟的方法。不只在调用外星方法时不用加锁,并且大大减小了代码持有锁的时间。 * 长时间地持有锁将影响性能(下降了程序的并发度),也会增长死锁的可能。 * @param n */ private void updateProgress2(int n){ ArrayList<ProgressListener> listenersCopy=null; synchronized (this){ listenersCopy=(ArrayList<ProgressListener> )listeners.clone(); } for(ProgressListener listener:listenersCopy){ listener.onProgress(n); } } @Override public void run(){ int n = 0, total = 0; byte[] buffer = new byte[1024]; try { while((n = in.read(buffer)) != -1) { out.write(buffer, 0, n); total += n; updateProgress(total); } out.flush(); } catch (IOException e) { e.printStackTrace(); } } }