当多个线程不会相互作用时,扩展多线程的应用是非常容易的,典型是在变量之间共享。当多线程相互作用时,那么多个问题将会出现,这些问题让应用处于线程不安全(在多线程的上下文中处于不正确的位置。)在这一章节,读者将会学习关于这些问题,和学习如何去解决它们,通过Java的同步语句。

2.1.线程的问题(The Problems with Threads)

java提供线程可以促进发展请求和可变的应用。然而,提供这些将会增长复杂的代价,一不小心,你的代码可能会出现难以琢磨的问题。例如:竞争状态、数据状态、缓存变量。

2.1.1竞争状态(Race Conditions)

当正确计算依赖于相对固定时间或多个线程交织的调试程序时,那么竞争状态就会出现。思考下面的代码片断,只要有确定的前提条件就会执行计算。

if (a == 10.0)
b = a / 2.0;

如果这个代码片断是在单一线程的上下文中是没有问题有,和当a和b是局部变量时,在多线程的上下文中也是没有问题的。然而,假设a和b是明确的实例或类是全局变量,而两个线程同时调用这个代码,那么就有问题了。

假设一个线程已经执行if(a==10.0)和它是关于去执行b = a /2.0时被程序的线程挂起,然而继续另一个线程改变了a的值。当被挂起的那个线程继续执行时,那么变量b就不是等于5了。(如a和b是本地变量,这个竞争的状态是不会出现的,因为每一个线程都会拷贝自己局部变量。)

这个代码片断是我们普遍的竞争状态的例子,是我们所知的检查-行动(check-then-act),这个就是决定下一步要做什么。前面的例子片断中,“check”是if(a==10.0)和“act”是b=a/2.0.

其它的竞争状态是读-修改-写(read-modify-write),新的状态来自于前一个状态。前一个状态是读,然后修改,最后更新影响到修改的结果,分为三个步骤来操作。然而,结合的操作是不可以分隔的。

一个普通的例子关于读-修改-写(read-modify-write),涉及的变量是在增加。例如,下面的代码片断,提供一个计算器,这个计算器是一个固定域的int类型(初始化为1),之后有两个线程线程同时调用这个代码。

public int getID()
{
return counter++;
}

尽管它看起来是一个操作,但是counter++实际上是三个操作:读counter的值,添加1到这个counter,和储蓄更新的值。读这个将是这个值的表现。

提供线程1请求getID()和读counter的值,在调度程序暂停之前它将会是1.现在有线程2运行,请求getID(),读counter的value(1),添加1到这个值,在counter中储蓄result(2),和返回1给请者。

这里关键的是,假定线程2重新恢复,添加1到先前读的value(1),储蓄result(2)在counter中,和返回1给请求者。因为线程1撤消了线程2执行的步骤,我们也就会失去了一次增加的数和一个不唯一的ID就会出现。为个方法是没有用的,有问题。

2.1.2数据竞争(Data Race)

一个竞争状态经常会在两个或多个线程共同使用本地内存时出现数据竞争的混淆,在此期间至少有一个线程是在访问写操作,而这些线程不能协调它们访问内存。当这些条件保持,访问顺序是非确定性的。

private static Parser parser;
public static Parser getInstance()
{
if (parser == null)
parser = new Parser();
return parser;
}

假设线程1第一次执行getInstance()方法。因为parser的值是null,所以线程1就会去实例Parser和赋值给parser.当线程2随后请求getInstance()方法时,它可能显示parser的值不为空的引用,所以就会返回一个parser的值。与此相同,线程2可能显示parser是空,和创建一个新的Parser对象。也就是说,一个行为必须早于另一个行为,在线程1写parser和线程2读parser时,数据竞争已经出现了。

2.1.3缓存变量(Cached Variable)

为了提高性能,编译器、java虚拟机和操作系统共同去缓存一个变量,在注册或本地处理器的缓存,而不是仅仅依赖于内存。每一个线程拥有它自己副本变量。当一个线程写这个变量时,它会写到自己的副本处;其它线程不可能看到这个副本更新的。

private static BigDecimal result;
public static void main(String[] args)
{
Runnable r = () ->
{
result = computePi(50000);
};
Thread t = new Thread(r);
t.start();
try
{
t.join();
}
catch (InterruptedException ie)
{
// Should never arrive here because interrupt() is never
// called.
}
System.out.println(result);
}

这个全局的变量result显示了一个缓存变量的问题。这个域访问一个工作的线程执行result=computePi(50000);在lambda的上下文,和在主线程执行System.out.println(result)。

这个工作线程可以存储computePi返回的值在它的副本result,而主线程打印出的是副本的值。主线程可能看不到result = computePi(50000)的值;注册和它的副本可能保留着默认的空值。此值将输出而不是结果的字符串表示(计算的PI值)。

源码下载:git@github.com:owenwilliam/Thread.git

2.同步(Synchronization)相关推荐

  1. 阻塞(block)/非阻塞(unblock) 同步(synchronization)/异步(asynchronization) 的区别

    阻塞和非阻塞,同步和异步[转载][经典] 转载于:https://www.cnblogs.com/wjc920/p/9256143.html

  2. 同步(Synchronization)和异步(Asynchronous)

    同步和异步都是基于应用程序和操作系统处理 IO事件所采用的方式.比如同步:是应用程序要直接参与 IO读写的操作.异步:所有的 IO读写交给操作系统去处理,应用程序只需要等待通知. 同步方式在处理 IO ...

  3. Linux多线程与同步

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 典型的UNIX系统都支持一个进程创建多个线程(thread).在Linux进程基础 ...

  4. access mysql 同步_使ACCESS数据库保持同步

    同步(Synchronization)是数据库在网络环境中应用所要涉及到的一个重要概念.其基本过程大致分以下几个步骤:首先把一个数据库设为可复制副本属性,使其成为设计正本(VB中称设计原版,ACCES ...

  5. java 同步块 抛出异常_java问题合集(一)

    垃圾回收算法 引用计数法,标记清除法,标记压缩清除法(Java中老年代采用),复制算法(Java中新生代采用),分代法(Java堆采用),分区算法. 重要的三句话: 垃圾回收器只知道释放那些经由new ...

  6. linux如何创建共享内存,linux实现共享内存同步的四种方法

    https://blog.csdn.net/sunxiaopengsun/article/details/79869115 本文主要对实现共享内存同步的四种方法进行了介绍. 共享内存是一种最为高效的进 ...

  7. linux 实现共享内存同步

    本文主要对实现共享内存同步的四种方法进行了介绍. 共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝.它是IPC对象的一种. 为了在多个进程间交换信息,内核专门留出了 ...

  8. synchronized关键字实现同步

    synchronized关键字的使用 Java语言提供了synchronized关键字,可以给方法或代码块进行加锁,从而实现同步. synchronized关键字取的锁都是对象锁,而不是把代码块或方法 ...

  9. linux socket read 接受缓存为空_Linux直接IO、缓存IO、阻塞与同步?

    Linux 基础知识回顾 用户空间和内核空间 现在操作系统都采用虚拟寻址,处理器先产生一个虚拟地址,通过地址翻译成物理地址(内存的地址),再通过总线的传递,最后处理器拿到某个物理地址返回的字节. 对 ...

  10. 阻塞io阻塞io_面试官:直接IO、缓存IO、阻塞与同步?

    点击上方"JAVA",星标公众号 重磅干货,第一时间送达 Linux 基础知识回顾 用户空间和内核空间 现在操作系统都采用虚拟寻址,处理器先产生一个虚拟地址,通过地址翻译成物理地址 ...

最新文章

  1. 李彦宏再提自动驾驶:是时候推动无人车商用了
  2. 开启ubuntu的SSH服务,使用终端远程控制
  3. 一个改进的小要求--限定随机数的个数,生成的随机数总和在一定范围内
  4. activemq的使用场景
  5. ejb 2.1 jboss_JBoss AS 8中的Java EE 7和EJB 3.2支持
  6. mybatis crud_MyBatis教程– CRUD操作和映射关系–第2部分
  7. awk 脚本_AWK单行代码和脚本可帮助您对文本文件进行排序
  8. leetcode python3 简单题119. Pascal's Triangle II
  9. IPv4与IPv6数据报格式详解
  10. 神泣单机服务器维护,神泣单机版
  11. css修改图标字体大小,css-更改AngularJS材质图标的图标大小
  12. 【91xcz】五方法助你轻松实现win8系统关机操作
  13. Python产生100个1—100的随机数放入列表Num中,输出列表中的数,然后将它们排序,并输出排序结果。
  14. 博途PLC 1200/1500PLC 工艺对象PID PID_Compact详细解读
  15. Allegro软件操作——绘制完成后检查内容,Gerberout
  16. python3 + Scrapy爬虫学习之创建项目
  17. 谷歌地图谷歌地图_为您的Google地图增添真实感
  18. 腾讯视频qlv转mp4
  19. MindSpore论坛十一月活动宣传
  20. 问题定义工具和三种问题定义思维模式

热门文章

  1. 在linux系统下安装redis
  2. Web调取摄像头拍照
  3. 【学习笔记】深入理解Linux内核第三版 ——第二章 内存寻址
  4. 关于C++标准库中的数据抽象
  5. CocoaAsyncSocket
  6. 基于天地图标点html教程,天地图WEB API入门指导
  7. 游戏开发之测试篇(C++)
  8. Hybrid 接口应用
  9. Vmware虚拟机宕机问题处理
  10. Android开发之跟踪应用更新大小