- 浏览: 87362 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (148)
- 全文检索 (1)
- java (29)
- xml (2)
- json (2)
- redis springmvc (1)
- Redis (5)
- 开发常识 (1)
- tomcat (2)
- 单元测试Junit (2)
- 设计模式 (2)
- spring (10)
- jvm (2)
- eclipse (4)
- echart (1)
- mybatis (1)
- mysql (3)
- web (1)
- js (2)
- PL/SQL (2)
- 其他 (1)
- 人生 (1)
- 安全 (2)
- jsp (2)
- 硬件电脑 (1)
- linux (3)
- git (10)
- oracle (8)
- ant (1)
- maven (2)
- 正则表达式 (2)
- chrome (1)
- 面试 (6)
- 多线程 (19)
- bug (11)
- java工具类 (3)
- 算法 (1)
- bug,git (1)
- shell (2)
- springmvc (2)
- Java8 (1)
- 消息队列-rocketmq (1)
- es (1)
- dubbo (0)
- spring cloud (0)
- hashmap (0)
- springboot (1)
- velocity (0)
线程的同步机制
(一)如果并发执行的多个线程间需要共享资源或交换数据,则这一组线程称为交互线程。
(二)交互线程并发执行时相互之间会干扰或影响其他线程的执行结果,因此交互线程之间需要有同步机制。
(三)交互线程之间存在两种关系:竞争关系和协作关系。
(四)对于竞争关系的交互线程间需要采用线程互斥方式解决共享资源冲突问题;
(五)对于协作关系的交互线程间需要采用线程同步方式解决线程间通信及因执行速度不同而引起的不同步问题。
(六)线程的同步机制包括线程互斥和线程同步,线程互斥是线程同步的特殊情况。
交互线程
无关线程与交互线程
并发线程之间可能是无关的,也可能是交互的。
无关的并发线程是指它们分别在不同的变量集合上操作。一个线程的执行与其他并发线程的进展无关,即一个并发线程不会改变另一个并发线程的变量值。
交互的并发线程是指他们共享某些变量,一个线程的执行可能影响其他线程的执行结果,交互的并发线程之间具有制约关系。因此,线程的交互必须是有控制的,否则会出现不正确的结果。
并发执行的交互线程间存在与时间有关的错误
无关线程间并发执行时不会产生与时间有关的错误。例如,奇数线程与偶数线程是两个并发执行的无关线程。程序每次运行,它们之间的执行次序可能会因线程调度而不同,但不会影响对方的结果值,奇数线程中绝不会输出偶数。
交互的并发线程执行时,由于它们在不同时刻对同一个共享变量进行操作,线程之间相互影响、相互干扰,因此计算结果往往取决于这一组并发线程的相对速度,各种与时间有关的错误就可能出现。
例如:将对银行账户的存款、取款操作分别设计成线程,存款线程与取款线程能够对同一个账户数据进行操作,此时该账户称为共享变量,并发执行的多个存款、取款线程间可能会产生与时间有关的错误。
package com.jbx.thread.account; public class Account { //账户类 private String name; //储户姓名 private double balance; //账户余额 public Account(String name) { this.name = name; this.balance = 0; } public String getName() { //返回账户名 return name; } public double getBalance() { //查看账户余额 return balance; } public void put(double value) { //存款操作,参数为存入金额 if(value>0) this.balance += value; //存款操作使余额值增加//存款操作,参数为取款金额,返回实际取到金额 } public double get(double value){ //取款操作,参数为取款金额,返回实际取到金额 if(value>0){ if(value<=this.balance) this.balance -= value; //取款操作使余额值减少 else{ value= this.balance; //取走全部余额 this.balance = 0; } return value; //返回实际取款额 } return 0; } } class Save extends Thread{ //存款线程类 private Account account; //账户 private double value; //存款金额 public Save(Account al ,double value){ this.account = al; this.value = value; } public void run(){ double howmatch = this.account.getBalance();//查看账户余额 this.account.put(this.value); try { sleep(1); //花费实际,线程执行被打断 } catch (InterruptedException e) {} System.out.println(this.account.getName()+"账户:现有"+howmatch+",存入"+this.value+",余额"+this.account.getBalance()); } } class Fetch extends Thread{ private Account account; // private double value; // public Fetch(Account al ,double value){ this.account = al; this.value = value; } public void run(){ double howmatch = this.account.getBalance();//查看账户余额 this.account.put(this.value); try { sleep(1); //花费实际,线程执行被打断 } catch (InterruptedException e) {} System.out.println(this.account.getName()+"账户:现有"+howmatch+",取走"+this.account.get(this.value)+",余额"+this.account.getBalance()); } public static void main(String[] args) { Account wang = new Account("wang"); (new Save(wang,100)).start(); //存100 (new Save(wang,100)).start(); //存200 (new Fetch(wang,100)).start(); //取300 } }
运行结果:
wang账户:现有0.0,存入100.0,余额300.0
wang账户:现有100.0,存入100.0,余额300.0
wang账户:现有200.0,取走100.0,余额200.0
解析:
main方法中对wang创建并启动了2个存款线程和1个取款线程。3个线程对象能够对同一个账户对象中数据进行操作,但每次运行的结果不确定,出现与时间有关的并发执行问题。
程序设计运行结果的讨论如下:
(1)运行结果不唯一,取决于线程调度
如果存/取款操作花费时间较短,线程执行没有被打断,即线程体中没有sleep延时,则程序运行结果不确定。取款线程实际取到的金额可能是0、100、200或300,取决于2个存款线程是否执行完成,与线程调度有关。一种可能的运行结果如下:
wang账户:现有0.0,存入100.0,余额100.0
wang账户:现有100.0,取走100.0,余额 0.0
wang账户:现有100.0,存入200.0,余额200.0
(2)线程执行被打断时出现错误
执行一个存/取款线程对象的run()方法,首先查看指定账户的现有金额,再进行存/取款操作,如果执行时间较长线程将会被打断。程序中用sleep(1)模拟线程执行被打断的情况,运行结果如下:
wang账户:现有0.0,存入100.0,余额100.0
wang账户:现有100.0,取走100.0,余额 0.0 //有错,三者数据不符
wang账户:现有100.0,存入200.0,余额200.0 //有错,三者数据不符
3个线程启动后都进入运行态,每个线程在查看了账户余额后被打断,尽管sleep时间只有1ms,很短暂,但足以改变线程状态,线程被迫让出处理器,暂停运行。系统调度其他线程运行,其他并发执行线程修改了同一账户的共享数据,导致线程再次运行时,该账户余额已不是其先前查看的金额,因而运行结果中查看金额、存/取金额和剩余金额三者数据不相符,这严重破坏了数据的完整性和一致性。
错误产生的原因是:多个线程交替访问同一个共享变量,干扰其他线程的执行结果。
避免出现错误的办法是:多个线程串行地、互斥地访问共享变量。
如果有两个线程同时分别对两个不同账户对象进行操作,则线程执行将不受干扰。例如:
(new Save(wang,200)).start();
(new Save(new Account("Li"),100)).start();
发表评论
-
ThreadPoolExecutor线程池参数详解
2019-01-09 00:06 458参考:https://www.cnblogs.com/wayt ... -
解决线程同步问题的思路
2018-07-31 16:35 402线程同步机制 操作系统中实现线程同步有一种工 ... -
发送线程与接收线程 速率一致
2018-08-07 09:17 373package com.jbx.xiezuo; /* ... -
线程间的协作关系与线程同步
2018-07-30 16:21 5101.线程间的协作关系 当一个进程中的多个线程为完成同 ... -
带互斥锁的并发线程执行流程
2018-07-30 12:38 426... -
线程间的竞争关系与线程互斥
2018-07-30 11:19 648线程间的竞争关系与线 ... -
3个线程,A,B,C,这三个线程分别只能打印 “a”,“b”,“c”,怎么让这三个线程依次打印“abc"
2018-07-17 11:19 337package com.jbx.thread; /* ... -
定时器与图形动画设计
2018-07-17 11:15 0动画(animation) 都是利用人眼的视 ... -
线程分类
2018-07-17 09:10 296线程分类: 线程可分为用户线程(user thread) ... -
线程优先级
2018-07-17 09:08 333Java提供10个等级的线程优先级,分别用1~10表示,优先级 ... -
设计滚动字演示线程状态及改变方法
2018-07-16 16:17 279本例演示线程对象的生 ... -
线程对象的生命周期
2018-07-16 11:28 337线程对象的生命周期 1.Thread.State类声明的线程状 ... -
声明实现Runnable接口的奇数/偶数序列线程
2018-07-11 14:02 404package com.jbx.thread; ... -
声明继承Thread类的奇数/偶数序列线程
2018-07-10 23:58 396package com.jbx.com; /** ... -
Java的线程对象Runnable接口和Thread类
2018-07-10 21:42 368Java支持内置的多线程机制。 Java语言包中的Runnab ... -
并发程序设计
2018-07-09 17:22 252并发程序设计 1.顺序程 ... -
线程介绍
2018-07-09 16:55 276线程 1.引入线程机制的 ... -
进程介绍
2018-07-09 15:38 350进程 进程的定义和属性 进程是一个可并发执行的 ... -
多线程
2018-07-09 14:31 292...
相关推荐
小实验一:编写一个没有线程同步机制的程序,调试程序,观察在执行程序的过程中,出现的问题并解答原因 小实验二:使用Windows互斥信号量操作函数解决上述线程并发问题,并分析、尝试和讨论线程执行体中有关信号量...
java的线程同步机制synchronized关键字的理解_.docx
实现操作系统的线程同步机制,包含代码,能运行。
锁正是基于这种思路实现的一种线程同步机制。 在对共享数据加锁后,每个线程在访问共享数据时必须先申请相应的锁。一旦获得锁后,就可以访问共享数据,并且一个锁同一时刻只能被一个线程持有,这意味着获得锁后不会...
为了解决多线程访问公共资源的冲突,开发人员需要采用线程同步机制,来避免同一时刻多个线程访问公共资源;该程序使用事件对象进行线程同步;事件对象分为人工重置事件对象和自动重置事件对象。
线程同步机制
本文档系操作系统课程线程同步机制的实验报告,实验内容包括:无同步机制、调用Mutex互斥变量、使用peterson软件法实现线程同步。完整的cpp源代码见文档附录。
线程同步机制实验报告.doc
实现Nachos的同步机制:锁和条件变量,并利用这些同步机制实现几个基础工具类
Java多线程同步机制研究分析
Java多线程同步机制的应用分析
JAVA多线程同步机制及其应用
Linux下的线程同步机制是实现多线程编程的关键。通过合理使用互斥锁、条件变量、读写锁和信号量等机制,可以确保多个线程之间的协调运行和数据一致性。在实际编程中,需要根据具体需求选择合适的同步机制并遵循最佳...
操作系统线程同步机制实验报告.doc
操作系统线程同步机制实验报告.pdf
利用mutex互斥变量实现线程同步机制,并输出循环1000000次所用的时间
操作系统实验报告—Windows线程同步机制.doc
设计一个多线程, 并且实现同步, 我理解的多线程需求如下: 1. 线程在Java端启动, 两个线程都调用C的方法 2. 有一个共同的数据, 被C的代码修改, 要求线程能对这个修改做同步, 即线程1