System.out.println 的多线程并发问题
假设println函数的參数为常量则不会出现线程并发问题,可是假设參数为表达式形式。则JVM在运行println函数的时候会分为几步来运行,从而造成并发问题。
例如以下样例所看到的:
package xiaoye.java; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong; public class Test
{
public static void main(String[] args)
{
ExecutorService pool = Executors.newFixedThreadPool(2);
Runnable t1 = new MyRunnable("张三", 2000);
Runnable t2 = new MyRunnable("李四", 3600);
Runnable t3 = new MyRunnable("王五", 2700);
Runnable t4 = new MyRunnable("老张", 600);
Runnable t5 = new MyRunnable("老牛", 1300);
Runnable t6 = new MyRunnable("老朱", 800);
//运行各个线程
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
pool.execute(t6);
//关闭线程池
pool.shutdown();
}
} class MyRunnable implements Runnable
{
private static AtomicLong aLong = new AtomicLong(10000); //原子量。每一个线程都能够自由操作
private String name; //操作人
private int data; //操作数 MyRunnable(String name, int data)
{
this.name = name;
this.data = data;
}
public void run()
{
Thread.yield();
System.out.println(name + "运行了" + data + "。当前剩余金额:" + aLong.addAndGet(data));
}
}
经过多次运行,当中一次结果例如以下:
李四运行了3600,当前剩余金额:15600
王五运行了2700,当前剩余金额:18300
老张运行了600。当前剩余金额:18900
老牛运行了1300,当前剩余金额:20200
老朱运行了800。当前剩余金额:21000
张三运行了2000。当前剩余金额:12000
对于
System.out.println(name + "运行了" + data + ",当前剩余金额:" + aLong.addAndGet(data));
经过反编译后得到例如以下实际代码:
System.out.println((new StringBuilder(String.valueOf(name))).append("运行了").append(data).append(",当前剩余金额:").append(aLong.addAndGet(data)).toString());
而对于System.out.println() 方法,它的运行代码例如以下:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
所以。输出过程须要经过两个步骤,转化字符串和同步输出。
实际上的运行过程是: 张三——李四——王五——老张——老牛——老朱,而实际上输出 张三 却是称为最后一个输出的。这是由于 张三 在或的锁之前被打断了。
假设我们想要输出正确的运行顺序,能够加一个显示锁:
package xiaoye.java; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class Test {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
Lock lock = new ReentrantLock(false);
Runnable t1 = new MyRunnable("张三", 2000, lock);
Runnable t2 = new MyRunnable("李四", 3600, lock);
Runnable t3 = new MyRunnable("王五", 2700, lock);
Runnable t4 = new MyRunnable("老张", 600, lock);
Runnable t5 = new MyRunnable("老牛", 1300, lock);
Runnable t6 = new MyRunnable("老朱", 800, lock);
// 运行各个线程
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
pool.execute(t6);
// 关闭线程池
pool.shutdown();
}
} class MyRunnable implements Runnable {
private static AtomicLong aLong = new AtomicLong(10000); // 原子量,每一个线程都能够自由操作
private Lock lock;
private String name; // 操作人
private int data; // 操作数 MyRunnable(String name, int data, Lock lock) {
this.name = name;
this.data = data;
this.lock = lock;
} public void run() {
lock.lock();
System.out.println(name + "运行了" + data + ",当前剩余金额:"
+ aLong.addAndGet(data));
lock.unlock();
}
}
这样,不管怎样执行,程序的输出顺序和操作的执行顺序都保持一致。
System.out.println 的多线程并发问题的更多相关文章
- java--HashMap多线程并发问题分析
并发问题的症状 多线程put后可能导致get死循环 从前我们的Java代码因为一些原因使用了HashMap这个东西,但是当时的程序是单线程的,一切都没有问题.后来,我们的程序性能有问题,所以需要变成多 ...
- HashMap多线程并发问题分析
转载: HashMap多线程并发问题分析 并发问题的症状 多线程put后可能导致get死循环 从前我们的Java代码因为一些原因使用了HashMap这个东西,但是当时的程序是单线程的,一切都没有问题. ...
- 由获取微信access_token引出的Java多线程并发问题
背景: access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token.开发者需要进行妥善保存.access_token的存储至少要保留512个字符空间.acces ...
- ThreadLocal 多线程并发,数据隔离
ThreadLocal: 创建一个线程本地变量. 本质:在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本. 优点:既实现多线程并发,游兼顾数据的安全性. 区别:Synchro ...
- Java 多线程 并发编程
一.多线程 1.操作系统有两个容易混淆的概念,进程和线程. 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进程的地址空间是互相隔离的:进程拥有各种 ...
- Java多线程并发
http://wangjianwei866.blog.163.com/blog/static/9295823201231665319314/ 基于以上网文,调整了一下格式,修改了一些标点和拼写错误. ...
- 多线程并发 synchronized对象锁的控制与优化
本文针对用户取款时多线程并发情境,进行相关多线程控制与优化的描述. 首先建立用户类UserTest.业务操作类SynchronizedTest.数据存取类DataStore,多线程测试类MultiTh ...
- 多线程并发执行任务,取结果归集。终极总结:Future、FutureTask、CompletionService、CompletableFuture
目录 1.Futrue 2.FutureTask 3.CompletionService 4.CompletableFuture 5.总结 ================正文分割线========= ...
- spring mvc 多线程并发
ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编写出优美的多线程程序. http://www.xuebuyuan.com/1628190.html 我们 ...
随机推荐
- 静态时序分析SAT
1. 背景 静态时序分析的前提就是设计者先提出要求,然后时序分析工具才会根据特定的时序模型进行分析,给出正确是时序报告. 进行静态时序分析,主要目的就是为了提高系统工作主频以及增加系统的稳定性.对 ...
- Codeforces--602A--Two Bases(水)
Two Bases Time Limit: 1000MS Memory Limit: 262144KB 64bit IO Format: %I64d & %I64u Submit St ...
- java静态类、静态方法、静态代码块,静态变量及实例方法,实例变量初始化顺序及内存管理,机制
1.当一个类被第一次使用时,它需要被类加载器加载,而加载过程涉及以下两点: (1)在加载一个类时,如果它的父类还未被加载,那么其父类必须先被加载: (2)当类加载到内存之后,按照在代码中的出现顺序执行 ...
- XDoclet学习
XDoclet可以通过你在java源代码中的一些特殊的注释信息,自动为你生成配置文件.源代码等等,例如web.ejb的部署描述文件.为你生成struts的struts-config.xml配置文件.j ...
- C# 利用反射和特性 来做一些事情
特性代码: [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public clas ...
- JavaScript Debug调试技巧
收藏于:https://blog.fundebug.com/2017/12/04/javascript-debugging-for-beginners/
- org.mybatis.spring.mapper.MapperScannerConfigurer$Scanner$1
不能加载或找不到 org.mybatis.spring.mapper.MapperScannerConfigurer$Scanner$1 经查证,是mybatis-spring-xxx.jar 这个版 ...
- OpenJDK源码研究笔记(十二):JDBC中的元数据,数据库元数据(DatabaseMetaData),参数元数据(ParameterMetaData),结果集元数据(ResultSetMetaDa
元数据最本质.最抽象的定义为:data about data (关于数据的数据).它是一种广泛存在的现象,在许多领域有其具体的定义和应用. JDBC中的元数据,有数据库元数据(DatabaseMeta ...
- cogs 1755. 爱上捉迷藏
1755. 爱上捉迷藏 ☆ 输入文件:kadun.in 输出文件:kadun.out 简单对比时间限制:0.001 s 内存限制:2 MB [背景] 乃们都玩过赛尔号吧……,那有木有玩 ...
- ArcGIS Engine 9.3启动程序报错
报错1: 没有注册类 (异常来自 HRESULT:0x80040154 解决办法: 由于是X64系统,将项目的生成目标该成X86就解决了. 报错2: 正试图在 OS 加载程序锁内执行托管代码.不要尝试 ...