假设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 的多线程并发问题的更多相关文章

  1. java--HashMap多线程并发问题分析

    并发问题的症状 多线程put后可能导致get死循环 从前我们的Java代码因为一些原因使用了HashMap这个东西,但是当时的程序是单线程的,一切都没有问题.后来,我们的程序性能有问题,所以需要变成多 ...

  2. HashMap多线程并发问题分析

    转载: HashMap多线程并发问题分析 并发问题的症状 多线程put后可能导致get死循环 从前我们的Java代码因为一些原因使用了HashMap这个东西,但是当时的程序是单线程的,一切都没有问题. ...

  3. 由获取微信access_token引出的Java多线程并发问题

    背景: access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token.开发者需要进行妥善保存.access_token的存储至少要保留512个字符空间.acces ...

  4. ThreadLocal 多线程并发,数据隔离

    ThreadLocal:  创建一个线程本地变量. 本质:在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本. 优点:既实现多线程并发,游兼顾数据的安全性. 区别:Synchro ...

  5. Java 多线程 并发编程

    一.多线程 1.操作系统有两个容易混淆的概念,进程和线程. 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进程的地址空间是互相隔离的:进程拥有各种 ...

  6. Java多线程并发

    http://wangjianwei866.blog.163.com/blog/static/9295823201231665319314/ 基于以上网文,调整了一下格式,修改了一些标点和拼写错误. ...

  7. 多线程并发 synchronized对象锁的控制与优化

    本文针对用户取款时多线程并发情境,进行相关多线程控制与优化的描述. 首先建立用户类UserTest.业务操作类SynchronizedTest.数据存取类DataStore,多线程测试类MultiTh ...

  8. 多线程并发执行任务,取结果归集。终极总结:Future、FutureTask、CompletionService、CompletableFuture

    目录 1.Futrue 2.FutureTask 3.CompletionService 4.CompletableFuture 5.总结 ================正文分割线========= ...

  9. spring mvc 多线程并发

    ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编写出优美的多线程程序. http://www.xuebuyuan.com/1628190.html 我们 ...

随机推荐

  1. ALTERA器件中复位电路实现之-异步复位同步化

    所谓异步复位同步化,就是我们通常说的异步复位同步撤除. 为了避免纯粹的同步复位和纯粹异步复位的问题,可以使用一种叫做同步化的异步复位,我们称其为第三类复位.这种复位完全结合了异步复位和同步复位的优势, ...

  2. Gym - 100625J Jailbreak 最短路+搜索

    http://codeforces.com/gym/100625/attachments/download/3213/2013-benelux-algorithm-programming-contes ...

  3. HIVE的几种优化

    5 WAYS TO MAKE YOUR HIVE QUERIES RUN FASTER 今天看了一篇[文章] (http://zh.hortonworks.com/blog/5-ways-make-h ...

  4. .net 项目分层及规范

       1.解决方案命名:公司简称+产品名称.如ABCSOft.BBS 2.解决方案文件夹:以数字排序例如:01.Web表示页面层:02.IBusinessLogic表示业务逻辑接口:03.Bussin ...

  5. 关于viewport详解

  6. NOIP 模拟赛

    NOIP 模拟赛 思路:求 n , m 的 gcd,然后用 n , m 分别除以 gcd:若 n 或 m 为偶数,则输出 1/2. 特别的,当 n = m = 1 时,应输出 1/1 #include ...

  7. Vijos——T 1082 丛林探险

    https://vijos.org/p/1082 描述 东非大裂谷中有一片神秘的丛林,是全世界探险家的乐园,著名黄皮肤探险家BB一直想去试试.正好我国科学家2005年4月将首次对东非大裂谷进行科考,B ...

  8. 洛谷 P1914 小书童——密码

    P1914 小书童——密码 题目背景 某蒟蒻迷上了“小书童”,有一天登陆时忘记密码了(他没绑定邮箱or手机),于是便把问题抛给了神犇你. 题目描述 蒟蒻虽然忘记密码,但他还记得密码是由一串字母组成.且 ...

  9. Yum数据库错误

    使用yum时提示数据库错误: /var/lib/rpm... open... db4 error from db->close:... 解决办法: 1.删除/var/lib/rpm目录下的__d ...

  10. 压缩感知——SP(subspace pursuit)重构算法前言翻译

    压缩感知是一种採样方法,它和变换编码类似,后者被广泛用于涉及到大规模数据採样的现代通信系统中.变换编码将高维空间中的输入信号.转换成很低的低维空间中的信号.变换编码器的样例有著名的小波变换和普遍存在的 ...