-
java中的线程同步_Java中线程同步的方法概述
2021-03-06 02:09:03多线程编程是非常有用的,但是当...在Java中,我们大致有以下三种方法来做到线程同步:同步代码块同步方法同步锁同步代码块当有两个线程并发访问并修改同一个文件时,如果不进行线程同步,就容易造成异常。我们可以...多线程编程是非常有用的,但是当使用多线程访问并修改可变资源时,如果不加控制,结果将变得难以预测
造成这个问题的原因可能是因为系统线程调度的随机性,也可能是编程不当。为了确保不读取到“脏数据”,我们有必要采用一定的手段,做到线程同步。
在Java中,我们大致有以下三种方法来做到线程同步:
同步代码块
同步方法
同步锁
同步代码块
当有两个线程并发访问并修改同一个文件时,如果不进行线程同步,就容易造成异常。我们可以引入同步监视器来解决这个问题,使用同步监视器的通用方法就是使用同步代码块。同步代码块的语法格式如下:
synchronized (obj){
//此处的代码为同步代码块
}
1
2
3
4
synchronized(obj){
//此处的代码为同步代码块
}
上面的语法格式中,括号中的obj就是同步监视器。
这段代码的含义是:线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。
任何时刻都只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成之后该线程会释放对该同步监视器的锁定。
虽然Java程序允许使用任何对象作为同步监视器,但是通常推荐使用可能被并发访问的共享资源充当同步监视器,因为这样可以阻止多个线程对同一个共享资源进行并发访问。
同步方法
与同步代码块对应的,Java的多线程安全支持还提供了同步方法,同步方法就是使用synchronized关键词来修饰某个方法,则该方法被称为同步方法。
对于被synchronized修饰的实例方法,无需显式指定同步监视器,同步方法的监视器就是调用该方法的对象。
通过使用同步方法就可以非常方便的实现线程安全的类,线程安全的类具有以下特征:
该类的对象可以被多个线程安全访问
每个线程调用该对象的任意方法之后都可以得到正确结果
每个线程调用该对象的任意方法之后,该对象状态仍然保持合理状态
需要注意的是:synchronized关键字可以修饰方法,可以修饰代码块,但不能修饰构造器、成员变量。
可变类的线程安全是以降低程序的运行效率为代价的,为了减少线程安全带来的负面效果,我们可以采用如下策略:
仅仅对会改变竞争资源的方法进行同步。
如果可变类有单线程环境和多线程环境两种不同的运行环境。则应该为该可变类提供两种不同的版本,即线程安全版本和线程不安全版本。例如StringBuilder和StringBuffer就是为了照顾单线程环境和多线程环境所提供的类。
同步锁
从Java5开始,Java提供了一种功能更加强大的线程同步机制,即通过显示地定义同步锁对象来实现通过不,在这种机制下,同步锁由Lock对象充当。
在实现线程安全的控制中,比较常用的是可重入锁(ReentrantLock),考虑下面这个示例:
class LockTest{
//获取可重入锁对象
private final ReentrantLock lock = new ReentrantLock();
public void test(){
lock.lock();
try{
//需要保证线程安全的代码
}finally {
lock.unlock();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
classLockTest{
//获取可重入锁对象
privatefinalReentrantLocklock=newReentrantLock();
publicvoidtest(){
lock.lock();
try{
//需要保证线程安全的代码
}finally{
lock.unlock();
}
}
}
虽然采用同步方法和同步代码块的范围机制使得多线程编程非常方便,而且还可以避免很多设计锁的常见编程错误,但是有时也需要以更为灵活的方式来使用锁。
Lock提供了同步方法和同步代码块所没有的其他功能,比如用于非块结构的tryLcok()方法、试图获取可中断锁的lockInterruptibly()方法以及获取超时失效锁的tryLock(long,TimeUnit)方法。
-
2017最新读取纯真IP数据库代码(C#)
2017-11-14 19:15:24在多线程应用环境中,假设 10 根线程访问同一个纯真 IP 数据库时,只会开辟 1 份缓存,给多根线程共享,避免了不必要的内存浪费。 注1:本模块代码,保证所有静态方法都是线程安全的,但不保证所有实例方法都是线程... -
JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)
2013-05-07 17:18:07对Java语言的每个语法都提供了一个或多个例程讲解 大量使用流程图表示程序的执行过程,使用结构图表示程序的内部状态 每章最后都给出了典型的练习题,让读者及时练习,巩固提高,并提供了参考答案 目录 第1篇 ... -
Java入门1·2·3:一个老鸟的Java学习心得.PART2(共3个)
2013-05-07 17:19:14对Java语言的每个语法都提供了一个或多个例程讲解 大量使用流程图表示程序的执行过程,使用结构图表示程序的内部状态 每章最后都给出了典型的练习题,让读者及时练习,巩固提高,并提供了参考答案 目录 第1篇 ... -
Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)
2013-05-07 17:20:12对Java语言的每个语法都提供了一个或多个例程讲解 大量使用流程图表示程序的执行过程,使用结构图表示程序的内部状态 每章最后都给出了典型的练习题,让读者及时练习,巩固提高,并提供了参考答案 目录 第1篇 ... -
Java并发编程的暗自努力(一)初步介绍
2017-08-26 22:45:29本系列属于学习路径记录,学习的主要内容来自并发编程网1....- 两个线程同时写同一个内存,在操作完成后会是什么结果呢?2. 多线程的优点 资源利用率好 场景:一个应用程序需要从本地文件系统中读取本系列属于学习路径记录,学习的主要内容来自并发编程网
1. Java的多线程和并发性
Java从一开始就支持多线程,因此Java开发者能常遇到异常问题场景,比如:
- 一个线程在读一个内存时,另一个线程正向该内存进行写操作,那进行读操作的那个线程会获得上面结果呢?
- 两个线程同时写同一个内存,在操作完成后会是什么结果呢?2. 多线程的优点
- 资源利用率好
- 场景:一个应用程序需要从本地文件系统中读取和处理文件:
- 读取一个文件要5秒
- 处理一个文件要2秒
- 多线程
- 5秒读取文件A
- 5秒读取文件B + 2秒处理文件A
- 2秒处理文件B
总的说来,CPU能够在等待IO的时候做一些其他的事情。(等待IO可能是磁盘IO、网络IO、用户输入等等)
- 程序设计在某些情况下更简单
现象:
- 在单线程情况下,如果你想处理上面说的读取和处理文件的顺序,你必须记录每个文件读取和处理的状态
- 在多线程情况下,每个线程处理一个文件的读取和操作,线程会在等待磁盘读取文件的过程中阻塞,其等待之时,其它线程能够使用CPU去处理已经读取完的文件- 程序响应更快
想象:
- 一个服务器应用,它的某一个端口监听进来的请求,当一个请求到来时,它去处理这个请求,然后再返回去监听// 服务器的流程如下 while(server is active){ listen for request process request }
如果一个请求需要占用大量时间处理
- 单线程时:在这段时间内新的客户端就无法发送请求给客户端
- 多线程时:监听线程把请求传递给工作者线程,然后立刻返回去监听。而工作者线程能够处理这个请求并发送另一个回复给客户端while(server is active){ listen for request hand request to worker thread }
这种方式,服务器线程能迅速地返回去监听。因此,更多的客户端能够发送请求给服务端,这个服务也变得响应更快
3. 多线程的代价
要使用多线程,就必须明确使用多线程获得的好处比所付出的代价大的时候,才使用多线程
设计更复杂
- 虽然有一些多线程应用程序比单线程的应用程序要简单,当一般来所,都是更复杂的。
- 在多线程访问共享数据的时候,这部分代码需要特别的注意
- 线程之间的交互往往非常复杂
- 不正确的线程同步产生的错误非常难被发现
上下文切换的开销
当CPU从执行一个线程切换到执行另一个线程时,它需要先存储当前线程的本地数据、程序指针等,然后载入另一个线程的本地数据、程序指针等,最后才开始执行。这种切换叫做“上下文切换”
增加资源的消耗
线程的运行所需资源。除了CPU,线程还需要一些内存来维持它本地的堆栈。它也需要占用操作系统中的资源来管理线程
小实验:尝试编写一个程序,它创建100个线程,这些线程只是wait,然后看看这个程序占用了多少内存?
4. 并发编程模型
并发模型与分布式系统之间的相似性
线程与进程之间具有很多相似的特性,这就是并发模型与分布式系统相似的原因
- 例如,为工作者们(线程)分配作业的模型一般与分布式系统中的负载均衡系统比较相似。同样,它们在日志记录、失效转移、幂等性等错误处理技术上也具有相似性。
模型1. 并行工作者
- 并行工作者模型:传入的作业会被分配到不同的工作者上
- 该模型中,委派者将传入的作业分配给不同的工作者,每个工作者完成整个任务(工作者们将运行在不同的线程上,甚至可能在不同的CPU上)
- 如果某个帽子生成作坊实现了并行工作者模型,则每个帽子都会由一个工人来生产,她收到帽子的制作要求,然后将负责完成整个帽子
在java应用系统中,并行工作者模型是很常见的
- java.util.concurrent包中的许多并发使用工具都是设计用于这个模型的
J2EE 应用服务器的设计中也能看到这个模型的踪迹
- 并行工作者模型的优点
简单、易于理解
- 并行工作者模型的缺点
共享状态可能会很复杂
当共享状态潜入工作者模型中——
要确保某个线程的修改能够对其它线程可见(数据修改要同步到主存中,不仅仅将数据保存在执行这个线程的CPU的缓存中)。线程需要避免竞态、死锁以及很多其它共享状态的并发性问题
在等待访问共享数据结构时,线程之间的互相等待将会丢失部分并行性
链接知识:非阻塞并发算法 也许可以降低竞争并提升性能,但实现比较困难
可持久化的数据结构是另一种选择。在修改的时候,可持久化的数据结构总是保护它的前一个版本不受影响。因此,如果多个线程指向同一个可持久化的数据结构,并且其中一个线程进行了修改,进行修改的线程会获得一个指向新结构的引用。所有其他线程保持对旧结构的引用,旧结构没有被修改并且因此保证一致性。
无状态的工作者
共享状态能够被系统中的其它线程修改,所以工作者每次需要的时候必须重读状态,以确保每次都能访问到最新的副本,不管共享状态是保存在内存还是外部数据库中。工作者是无状态的(无法在内部保存这个状态)。
任务顺序是不确定的
并行工作者模式的另一个缺点是执行顺序是不确定的
模型2.流水线模式
- 每个工作者只负责作业中的部分工作。当完成了自己的这部分工作时工作者会将作业转发给下一个工作者。
通常使用非阻塞的IO来设计使用流水线并发模型的系统。非阻塞IO意味着,一旦某个工作者开始一个IO操作的时候(比如读取文件或从网络连接中读取数据),这个工作者不会一直等待IO操作的结束。IO操作速度很慢,所以等待IO操作结束很浪费CPU时间。此时CPU可以做一些其他事情。当IO操作完成的时候,IO操作的结果(比如读出的数据或者数据写完的状态)被传递给下一个工作者。
有了非阻塞IO,就可以使用IO操作确定工作者之间的边界。工作者会尽可能多运行直到遇到并启动一个IO操作。然后交出作业的控制权。当IO操作完成的时候,在流水线上的下一个工作者继续进行操作,直到它也遇到并启动一个IO操作。
反应器,事件驱动系统
采用流水线并发模型的系统有时称为反应器系统或事件驱动系统。系统内的工程对系统内出现的事件做出反应,这些事件也有可能来自外部世界或者发自其他工作者。事件可以是传入的HTTP请求,也可以是某个文件成功加载到内存中等。
流水线模型的优点
无需共享的状态
工作者之间无需共享的状态,意味着实现的时候无需考虑所有因并发访问共享对象产生的并发性问题
有状态的工作者
当工作者知道了没有其他线程可以修改它们的数据,工作者可以变成有状态的。
较好的硬件整合
单线程代码在整合底层硬件的时候往往具有更好的优势。
合理的作业顺序
基于流水线并发模型实现的并发系统,在某种程度上是有可能保证作业的顺序的。作业的有序性使得它更容易地推出系统在某个特定时间点的状态。更进一步,你可以将所有到达的作业写入到日志中去。
流水线模型的缺点
流水线并发模型最大的缺点是作业的执行往往分布到多个工作者上,并因此分布到项目中的多个类上。这样导致在追踪某个作业到底被什么代码执行时变得困难。
函数式并行
函数可以看作是”代理人(agents)“或者”actor“,函数之间可以像流水线模型(AKA 反应器或者事件驱动系统)那样互相发送消息。某个函数调用另一个函数,这个过程类似于消息发送。
- 资源利用率好
-
1350多个精品易语言模块
2015-07-26 12:21:30ZCL_多线程类1.01.ec ZCL_控件类库1.01.ec ZCL_文件读写 1.01.ec ZCL_核库函数1.01.ec zip.ec Z计算器.ec [神2也教你学E] - 可执行动态载入&输出其他文件模块.ec _仿真shell库.ec √功能键状态√.ec √取功能键状态... -
1350多个精品易语言模块提供下载
2011-06-06 17:51:09ZCL_多线程类1.01.ec ZCL_控件类库1.01.ec ZCL_文件读写 1.01.ec ZCL_核库函数1.01.ec zip.ec Z计算器.ec [神2也教你学E] - 可执行动态载入&输出其他文件模块.ec _仿真shell库.ec √功能键状态√.ec √取功能键状态... -
Java开发技术大全(500个源代码).
2012-12-02 19:55:48invokeMethod.java 同一个类中调用方法示例 invokeOther.java 类的外部调用方法示例 invokeStaticMethod.java 调用静态方法示例 localVariable.java 演示局部变量 localVSmember.java 局部变量与成员变量同名... -
成百上千个Java 源码DEMO 4(1-4是独立压缩包)
2017-03-29 17:40:59数字证书:从文件中读取数字证书,生成文件输入流,输入文件为c:/mycert.cer,获取一个处理X.509证书的证书工厂…… Java+ajax写的登录实例 1个目标文件 内容索引:Java源码,初学实例,ajax,登录 一个Java+ajax写的... -
成百上千个Java 源码DEMO 3(1-4是独立压缩包)
2017-03-29 17:39:54数字证书:从文件中读取数字证书,生成文件输入流,输入文件为c:/mycert.cer,获取一个处理X.509证书的证书工厂…… Java+ajax写的登录实例 1个目标文件 内容索引:Java源码,初学实例,ajax,登录 一个Java+ajax写的... -
尹成Python27天入门到项目实战
2020-12-05 17:04:22多进程多线程综合实战读取CSV写入csv单线程统计行数多线程统计行数多进程统计行数多线程检索数据第一步多线程检索赵琳多线程检索找到通知其他人退出多线程检索开放数据并保存同一个文件作业day24up ... -
多人聊天器求大神代码!
2016-11-25 05:47:02描述:服务端使用链表记录当前客户端的会话连接,多个线程访问同一个链表,采用互斥锁来控制。 链表包含下列信息:客户端的ip地址、连接时间,用户名。 1.1.6链表记录动态维护 属性:任选 描述:服务端能够... -
第一行代码Java:视频讲解版.李兴华(带详细书签).pdf
2018-10-25 16:42:08全书分为15章,包括Java简介、程序基本概念、面向对象基本概念、面向对象高 级知识、包及访问控制权限、异常的捕获及处理、Eclipse开发工具、Java新特性、多线程、Java常用类库、Java IO编程、Java网络编程、Java类... -
Visual C++范例大全 本书全部源码(1~17章)共406个实例
2010-02-06 15:27:35实例269——使用多线程进行文件搜索 实例270——获取当前系统的所有进程 实例271——实现应用程序在系统中只能运行一个实例 实例272——获取所有打开窗口程序的句柄、类名及标题 实例273——创建和终止进程 ... -
Visual C++范例大全 本书全部源码打包(1~17章)共406个实例
2013-08-18 10:08:00实例269——使用多线程进行文件搜索 实例270——获取当前系统的所有进程 实例271——实现应用程序在系统中只能运行一个实例 实例272——获取所有打开窗口程序的句柄、类名及标题 实例273——创建和终止进程... -
易语言模块914个
2018-03-12 20:00:21ZCL_多线程类1.01.ec ZCL_控件类库1.01.ec ZCL_文件读写1.01.ec ZCL_核库函数1.01.ec zip.ec zip压缩.ec 万能注册验证模块.ec 世恒通用安装系统文件压缩模块.ec 个性信息框1.1.ec 个性信息框1.21.ec 个性... -
多人聊天器,求大神发源代码!初来乍到,小弟实在不是很懂,望大神指教!在线等大神!
2016-11-24 11:20:28描述:服务端使用链表记录当前客户端的会话连接,多个线程访问同一个链表,采用互斥锁来控制。 链表包含下列信息:客户端的ip地址、连接时间,用户名。 1.1.6链表记录动态维护 属性:任选 描述:服务端... -
1345个易语言模块
2012-01-27 19:41:59ZCL_多线程类1.01.ec ZCL_控件类库1.01.ec ZCL_文件读写 1.01.ec ZCL_核库函数1.01.ec zip.ec Z计算器.ec [神2也教你学E] - 可执行动态载入&输出其他文件模块.ec _仿真shell库.ec √功能键状态√.ec √取功能键状态... -
易语言模块大全(914个)
2011-08-22 18:48:16ZCL_多线程类1.01.ec ZCL_控件类库1.01.ec ZCL_文件读写1.01.ec ZCL_核库函数1.01.ec zip.ec 个性信息框1.21.ec 个性信息框1.ec 个性化电脑模块.ec 互联网扩展模块1.1.ec 五笔编码查询模块.ec 代码编辑器部分模块.ec... -
java源码包---java 源码 大量 实例
2013-04-18 23:15:26数字证书:从文件中读取数字证书,生成文件输入流,输入文件为c:/mycert.cer,获取一个处理X.509证书的证书工厂…… Java+ajax写的登录实例 1个目标文件 内容索引:Java源码,初学实例,ajax,登录 一个Java+ajax写... -
Python Cookbook 第三版中文
2016-01-25 17:02:3110.4 将模块分解成多个文件 410 10.5 让各个目录下的代码在统一的命名空间下导入 413 10.6 重新加载模块 415 10.7 让目录或zip文件成为可运行的脚本 416 10.8 读取包中的数据文件 417 10.9 添加目录到sys.path中 418... -
java源码包2
2013-04-20 11:28:17数字证书:从文件中读取数字证书,生成文件输入流,输入文件为c:/mycert.cer,获取一个处理X.509证书的证书工厂…… Java+ajax写的登录实例 1个目标文件 内容索引:Java源码,初学实例,ajax,登录 一个Java+ajax... -
Visual.C#.编程精彩百例
2013-01-07 12:09:39实例11 一个有趣栈类的实现 实例12 垃圾收集器管理与应用 实例13 垃圾收集器算法控制与使用 实例14 调用栈记录异常点 实例15 使用C#异常的栈跟踪 实例16 运行期间检测变量类型 实例17 常用值类型的原型定义 ... -
Python Cookbook
2013-07-31 22:33:2617.5 在多线程环境中使用SWIG生成的模块 603 17.6 用PySequence_Fast将Python序列转为 C数组 604 17.7 用迭代器逐个访问Python序列的元素 608 17.8 从Python可调用的C函数中返回None 611 17.9 用gdb调试动态载入... -
超级有影响力霸气的Java面试题大全文档
2012-07-18 09:47:04换言之,很可能数个使用者在执行某个 Stateless Session Bean 的 methods 时,会是同一个 Bean 的 Instance 在执行。从内存方面来看, Stateful Session Bean 与 Stateless Session Bean 比较, Stateful Session ... -
JAVA上百实例源码以及开源项目源代码
2018-12-11 17:07:42数字证书:从文件中读取数字证书,生成文件输入流,输入文件为c:/mycert.cer,获取一个处理X.509证书的证书工厂…… Java+ajax写的登录实例 1个目标文件 内容索引:Java源码,初学实例,ajax,登录 一个Java+ajax写的... -
JAVA上百实例源码以及开源项目
2016-01-03 17:37:40数字证书:从文件中读取数字证书,生成文件输入流,输入文件为c:/mycert.cer,获取一个处理X.509证书的证书工厂…… Java+ajax写的登录实例 1个目标文件 内容索引:Java源码,初学实例,ajax,登录 一个Java+ajax写... -
java源码包3
2013-04-20 11:30:13数字证书:从文件中读取数字证书,生成文件输入流,输入文件为c:/mycert.cer,获取一个处理X.509证书的证书工厂…… Java+ajax写的登录实例 1个目标文件 内容索引:Java源码,初学实例,ajax,登录 一个Java+ajax...