Java并发编程基础
1. 并发
1.1. 什么是并发?
并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力。如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互性将大大改善。现代的PC都有多个CPU或一个CPU中有多个核。是否能合理运用多核的能力将成为一个大规模应用程序的关键。
1.2. 进程 vs. 线程
进程是以独立于其他进程的方式运行的,进程间是互相隔离的。一个进程无法直接访问另一个进程的数据。进程的资源诸如内存和CPU时间片都是由操作系统来分配。
线程又被称为轻量级进程。每个线程有它独自的调用栈, 但是在同一进程下的线程又能互相访问它们间的共享数据。每个线程都有它独自的缓存。如果一个线程读取了某些共享数据,那么它将这些数据存放在自己的缓存中以供将来再次读取。
一个JAVA应用程序默认以一个进程的形式运行着。在一个JAVA程序中,你将协同多个不同的线程一起完成并行运算或实现异步行为。
2. 并发的改进与问题
2.1. 并发性能的上限
之所以并发号称能较快的完成某些任务是因为这些任务能被分组成多个子任务,并且这些子任务能被并行的执行。所以一个任务的实际运行效率将受限于该任务中能并行执行的部分。
一个程序理论上可达到的最高并发性能可通过以下一个被称为Amdahl 定律来计算出:
设F为一个程序中不能被并行执行的百分比,N是处理器的数量,那么理论上该程序能获得的最高并发性能将可能是1/ (F+ ((1-F)/n)).
2.2. 并发问题
线程有独自的调用栈,但是又能互相访问共享的数据。所以这里你会遇到两个问题,可见性和访问。
可见性问题发生于如果线程A先读取了某些共享数据,之后线程B对这些数据进行了修改,那么线程A可能看不到线程B对这数据的改动。
访问问题发生于于 多个线程同时访问修改同一个共享数据。
可见性及访问问题将导致
- 活跃性失败:由于并发访问数据导致程序无任何反应。 譬如,死锁。
- 安全性失败:程序创建了错误的数据。
3. JAVA中的并发
3.1. 进程与线程
一个JAVA程序默认以一个线程运行在自己的进程中。JAVA语言通过Thread相关代码来支持线程。JAVA程序可通过Thread这个类来创建线程。从JAVA1.5起,在java.util.concurrent中提供了改进的并发库。
3.2. 锁和线程同步
JAVA提供了“锁” 机制来保护代码片段免于被多个线程在同一时刻运行。最简单的锁住一个方法或一个类就是在该方法或类前添加synchronized关键字
在Java中synchronized关键字保证了:
- 在同一时刻,只有单个线程能访问这块代码
- 每个进入这同步代码块的线程都将能看到之前持有相同锁进入的线程的所做的改动
对于线程间的可靠通讯及互斥访问 来说,同步是非常必要的
你可以在定义方法时使用synchronized关键字。这个关键字保证了同一时刻只有一个线程能运行这个方法。其他同样调用了这个方法的线程将被阻塞直到第一个线程离开这个方法。
public synchronized void critial() {
// some thread critical stuff
// here
}
你同样也能用synchronized关键字来保护方法中的代码块。这块代码将由一个关键对象来保护,该关键对象可以是个string或其他object。这个关键对象就被称为 lock。所有被相同lock保护的代码在同一时刻只能被单个线程执行。
举例来说,以下的数据结构将保证同时只有单个线程可以访问add和next方法
package de.vogella.pagerank.crawler; import java.util.ArrayList;
import java.util.List;
/*** Data structure for a web crawler. Keeps track of the visited sites and keeps
* a list of sites which needs still to be crawled.
* @author Lars Vogel
*/
<pre>public class CrawledSites {
private List<String> crawledSites = new ArrayList<String>();
private List<String> linkedSites = new ArrayList<String>(); public void add(String site) {
synchronized (this) {
if (!crawledSites.contains(site)) {
linkedSites.add(site);
}
}
}
/** * Get next site to crawl. Can return null (if nothing to crawl) */
public String next() {
if (linkedSites.size() == 0) {
return null;
}
synchronized (this) {
<em>// Need to check again if size has changed</em>
if (linkedSites.size() > 0) {
String s = linkedSites.get(0);
linkedSites.remove(0);
crawledSites.add(s);
return s;
}
return null;
}
} }
3.3. Volatile
如果一个变量声明时使用了volatile 关键字,那么该关键字保证了,任何读取该变量的线程都将读到最新写进该变量的值。但volatile 关键字不会保证变量上的任何互斥访问
As of Java 5 write access to a volatile variable will also update non-volatile variables which were modified by the same thread. This can also be used to update values within a reference variable, e.g. for a volatile variable person. In this case you must use a temporary variable person and use the setter to initialize the variable and then assign the temporary variable to the final variable. This will then make the address changes of this variable and the values visible to other threads.
因为在JAVA 5中,对一个声明了volatile 的变量进行写操作会导致该写操作所在该线程之前所有的对非volatile 变量的修改进行同步更新。这也可用来更新引用类型变量。例如,有个volatile 类型的变量person。你必须创建一个临时变量person,然后调用SETTER方法来初始化这变量然后将临时变量赋值给final变量。 这会使这个变量的地址发生改变并且此变量的值对于其他线程变为可见。
4. JAVA 内存模型
4.1. 概览
JAVA内存模型描述了线程内存与主存间的通讯关系。
JAVA内存模型定义了线程内的内存改变将怎样传递到其他线程的规则,同样也定义了线程内存与主存进行同步的场景,也描述了哪些操作属于原子操作及操作间的顺序。
4.2. 原子操作
一个原子操作是指一个执行时不会被其他操作影响到的最小单位的操作。
JAVA语言规范保证了对一个变量的读和写操作都是原子的(除了LONG和DOUBLE类型的变量)。对于LONG或DOUBLE类型的变量,只有当这些变量声明时使用了volatile关键字才是原子的
假设定义了一个INT变量I,那么在java中,I++操作不是一个原子操作。同样,这对于其他数字类型的变量也都不是一个原子操作。
I++操作先从I中读取了当前值(这是个原子操作)然后再让它加上1写回(原子操作)。但是在读和写这两个操作间,I的值有可能被改变。
从JAVA1.5起,JAVA提供了原子变量,例如AtomicInteger 或 AtomicLong 都提供了类似 getAndDecrement()
, getAndIncrement()
及 getAndSet()等原子方法。
4.3.同步代码块的内存更新
JAVA内存模型保证了每个进入相同锁同步块的线程都能看到之前进入的其他所有线程修改的结果。
Java并发编程基础的更多相关文章
-
并发-Java并发编程基础
Java并发编程基础 并发 在计算机科学中,并发是指将一个程序,算法划分为若干个逻辑组成部分,这些部分可以以任何顺序进行执行,但与最终顺序执行的结果一致.并发可以在多核操作系统上显著的提高程序运行速度 ...
-
Java并发编程--基础进阶高级(完结)
Java并发编程--基础进阶高级完整笔记. 这都不知道是第几次刷狂神的JUC并发编程了,从第一次的迷茫到现在比较清晰,算是个大进步了,之前JUC笔记不见了,重新做一套笔记. 参考链接:https:// ...
-
Java并发编程基础-线程安全问题及JMM(volatile)
什么情况下应该使用多线程 : 线程出现的目的是什么?解决进程中多任务的实时性问题?其实简单来说,也就是解决“阻塞”的问题,阻塞的意思就是程序运行到某个函数或过程后等待某些事件发生而暂时停止 CPU 占 ...
-
java并发编程基础概念
本次内容主要讲进程和线程.CPU核心数和线程数.CPU时间片轮转机制.上下文切换,并行和并发的基本概念以及并发编程的好处和注意事项,为java并发编程打下扎实基础. 1.什么是进程和线程 1.1 进程 ...
-
多线程(一)java并发编程基础知识
线程的应用 如何应用多线程 在 Java 中,有多种方式来实现多线程.继承 Thread 类.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实现带 ...
-
Java并发编程基础三板斧之Semaphore
引言 最近可以进行个税申报了,还没有申报的同学可以赶紧去试试哦.不过我反正是从上午到下午一直都没有成功的进行申报,一进行申报 就返回"当前访问人数过多,请稍后再试".为什么有些人就 ...
-
Java并发编程基础之volatile
首先简单介绍一下volatile的应用,volatile作为Java多线程中轻量级的同步措施,保证了多线程环境中“共享变量”的可见性.这里的可见性简单而言可以理解为当一个线程修改了一个共享变量的时候, ...
-
java并发编程基础——线程的创建
一.基础概念 1.进程和线程 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程.(进程是资源分配的最小单位) 线程:同一类线程共享代码和数据 ...
-
Java并发编程基础-ReentrantLock的机制
同步锁: 我们知道,锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源,在Lock接口出现之前,Java应用程序只能依靠synchronized关键字来实现同步锁 ...
随机推荐
-
char 型二维数组
char FutureFunc[][16] = {"XMA","ZIG","PEAK","PEAKBARS"," ...
-
作业七:团队项目——Alpha版本冲刺阶段009
今日安排:组内成员讨论 今日进度:组内成员讨论分工细节以及可能遇到的问题,并提出解决方案
-
C++ MFC打开文件的流程
打开文件的步骤如下: 弹出打开文件对话框 -> 获取选择的文件,并将文件显示在视图中. 我们程序中经常需要定制的操作如下: 1. 定制弹出的文件对话框,例如需要修改打开文件的类型或扩展名 2. ...
-
mapreduce job提交流程源码级分析(三)
mapreduce job提交流程源码级分析(二)(原创)这篇文章说到了jobSubmitClient.submitJob(jobId, submitJobDir.toString(), jobCop ...
-
leetcode 100 Same Tree ----- java
Given two binary trees, write a function to check if they are equal or not. Two binary trees are con ...
-
LinqToExcel: LINQ查询Excel电子表格
Linq的强大人所共知,能不能将Linq扩展到读取excel呢? 答案当然是肯定的. LinqToExcel就是一个实现了使用Linq语法查询excel表格的.net开源类库. 在nuget搜索下载安 ...
-
复习IOS多线程知识
线程的注意点 1.不要同时开太多的线程(1~3条线程即可,不要超过5条) 2.线程概念 * 主线程 : UI线程,显示.刷新UI界面,处理UI控件的事件 * 子线程 : 后台线程,异步线程 3.不要把 ...
-
OpenCV源码阅读(1)---matx.h---mat类与vec类
matx.h matx类是opencv中的一个基础类,其位于core模块中,所执行的操作时opencv矩阵和向量的运算.如果熟悉基于matlab的图像处理,那么很容易想到,所有对图像的操作归根结底都是 ...
-
Java知多少(56)线程模型
Java运行系统在很多方面依赖于线程,所有的类库设计都考虑到多线程.实际上,Java使用线程来使整个环境异步.这有利于通过防止CPU循环的浪费来减少无效部分. 为更好的理解多线程环境的优势可以将它与它 ...
-
JZOJ1517. 背包问题
这个题,乍一看感觉挺神的(其实真挺神的),其实是个简单的分组背包(如果恍然大悟就不用接着看了) 取连续的一段是这道题最难以处理的地方,但是观察到物品数量不多<=100(如果恍然大悟就不用接着看了 ...