当多个线程不会相互作用时,扩展多线程的应用是非常容易的,典型是在变量之间共享。当多线程相互作用时,那么多个问题将会出现,这些问题让应用处于线程不安全(在多线程的上下文中处于不正确的位置。)在这一章节,读者将会学习关于这些问题,和学习如何去解决它们,通过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. [十九]JavaIO之PipedReader 和 PipedWriter
  2. 在云服务器上持续运行springboot项目
  3. EduCoder Linux之文件/目录搜索
  4. Web开发者的最佳开发调试环境Chrome
  5. java性能测试jmh
  6. FFmpeg — 屏幕录制器材
  7. 【零基础学Java】—Map集合概述(四十三)
  8. 4号线地铁站点列表_【暖房】6号线酒店式白领公寓;房山地铁站品牌公寓;4号线分散式公寓;朝阳门附近整租房源...
  9. 查询一个表中一个字段相同的数据_最实用MySQL 查询当天、本周,本月、上一个月的数据...
  10. DIY开源mini桌面i3结构3D打印机--分析
  11. java web学习心得
  12. eNSP入门-基本配置
  13. 地表蒸散发(ET)数据下载网站汇总
  14. 曲面局部理论介绍——从曲面的概念、基本形式到高斯曲率及其 Pthyon 计算
  15. codeforces 1328 B. K-th Beautiful String
  16. Redhat之NIS
  17. 64位系统装32位mysql有问题吗_32位系统部署到64位下常见问题及解决
  18. 在线文档 - Google drive
  19. Stderr: VBoxManage.exe: error: VT-x is not available (VERR_VMX_NO_VMX) VBoxM
  20. CSR8670项目实战:BlueHeart心率监测耳机

热门文章

  1. 利用百度搜索结果爬取邮箱
  2. chattr和lsattr的基本用法
  3. Single Number leetcode java
  4. Winform控件学习笔记【第四天】——WebBrowser
  5. 使用XML绑定下拉列表
  6. tomcat使用中出现的问题及其解决之道
  7. HCIE-RS面试--RSTP为什么优于STP
  8. mysql硬盘复制无法启动_磁盘的移动导致MySQL数据启动失败
  9. 078、Docker 最常用的监控方案(2019-04-25 周四)
  10. 深入理解【缺页中断】及FIFO、LRU、OPT这三种置换算法