关于System.out.println的多线程并发问题
2014-11-07 10:43
573 查看
本文转载自
IT江湖,原文链接:Click me
如果println函数的参数为常量则不会出现线程并发问题,但是如果参数为表达式形式,则JVM在执行println函数的时候会分为几步来执行,从而造成并发问题。
如下例子所示:
Java
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()
{
System.out.println(name + "执行了" + data + ",当前余额:" + aLong.addAndGet(data));
}
}
执行的结果为:
张三执行了2000,当前余额:12000
李四执行了3600,当前余额:15600
王五执行了2700,当前余额:18300
老牛执行了1300,当前余额:20200
老朱执行了800,当前余额:21000
老张执行了600,当前余额:18900
通过反编译,上面println函数的主要处理过程分为下面三步:
Java
long l = aLong.addAndGet(data);
String sb =new StringBuilder(String.valueOf(name))).append("执行了").append(data).append(",当前余额:").append(l);
synchronized (this)
{
print(sb.toString);
newLine();
}
所以可以看出上面程序的执行顺序为:、
张三执行完->李四执行完->王五执行完->老张只执行到加操作,被老牛打断->老牛执行完->老朱执行完->老张执行剩下的输出操作
要想有一个正常的输出顺序,应该修改代码为:
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 String name; //操作人
private int data; //操作数
private Lock lock;
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();
}
}
结果为:
张三执行了2000,当前余额:12000
王五执行了2700,当前余额:14700
老张执行了600,当前余额:15300
老牛执行了1300,当前余额:16600
老朱执行了800,当前余额:17400
李四执行了3600,当前余额:21000
【注意】这里使用了一个对象锁,来控制对并发代码的访问。不管运行多少次,执行次序如何,最终余额均为21000,这个结果是正确的。有关原子量的用法很简单,关键是对原子量的认识,原子仅仅是保证变量操作的原子性,但整个程序还需要考虑线程安全的。
IT江湖,原文链接:Click me
如果println函数的参数为常量则不会出现线程并发问题,但是如果参数为表达式形式,则JVM在执行println函数的时候会分为几步来执行,从而造成并发问题。
如下例子所示:
Java
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()
{
System.out.println(name + "执行了" + data + ",当前余额:" + aLong.addAndGet(data));
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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() { System.out.println(name + "执行了" + data + ",当前余额:" + aLong.addAndGet(data)); } } |
张三执行了2000,当前余额:12000
李四执行了3600,当前余额:15600
王五执行了2700,当前余额:18300
老牛执行了1300,当前余额:20200
老朱执行了800,当前余额:21000
老张执行了600,当前余额:18900
通过反编译,上面println函数的主要处理过程分为下面三步:
Java
long l = aLong.addAndGet(data);
String sb =new StringBuilder(String.valueOf(name))).append("执行了").append(data).append(",当前余额:").append(l);
synchronized (this)
{
print(sb.toString);
newLine();
}
1 2 3 4 5 6 7 | long l = aLong.addAndGet(data); String sb =new StringBuilder(String.valueOf(name))).append("执行了").append(data).append(",当前余额:").append(l); synchronized (this) { print(sb.toString); newLine(); } |
张三执行完->李四执行完->王五执行完->老张只执行到加操作,被老牛打断->老牛执行完->老朱执行完->老张执行剩下的输出操作
要想有一个正常的输出顺序,应该修改代码为:
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 String name; //操作人
private int data; //操作数
private Lock lock;
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();
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 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 String name; //操作人 private int data; //操作数 private Lock lock; 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(); } } |
张三执行了2000,当前余额:12000
王五执行了2700,当前余额:14700
老张执行了600,当前余额:15300
老牛执行了1300,当前余额:16600
老朱执行了800,当前余额:17400
李四执行了3600,当前余额:21000
【注意】这里使用了一个对象锁,来控制对并发代码的访问。不管运行多少次,执行次序如何,最终余额均为21000,这个结果是正确的。有关原子量的用法很简单,关键是对原子量的认识,原子仅仅是保证变量操作的原子性,但整个程序还需要考虑线程安全的。
相关文章推荐
- 关于System.out.println的多线程并发问题
- java 关于System.out.println的多线程并发问题
- System.out.println 的多线程并发问题
- System.out.println 的多线程并发问题
- java 中关于System.out.println()的问题
- 关于使用System.out.println()向控制台输出数据和使用out.println()向页面输出数据效率的问题
- 关于java中并发时引起的问题(多线程问题)
- 恒大的一个题,关于System.out.print和System.err.println以及静态和构造函数的
- java的类型转换问题。int a = 123456;short b = (short)a;System.out.println(b);为什么结果是-7616?
- 关于java三元运算符,(i%5==0)?System.out.println():System.out.print(" "); does not work!
- 用eclipse开发servlet,使用System.out.println()在console打印不出数据的问题
- Java学习笔记之关于System.out.println()的括号中的内容
- Android问题:System.out.println('a'+'b')和System.out.println('1'+'2');
- 解决MyEclipse中alt + / 快捷键不能用的问题;System.out.println快捷键;
- 多线程:为什么在while循环中加入System.out.println,线程可以停止
- 关于i--与System.out.println()联合使用时可能出现的异常情况
- 问题 System.out.println(versionName);输出结果:System.out: INSTANT_RUN
- ASP.NET 中关于 System.OutOfMemoryException 的问题与解决方法
- 使用system.out.println输出类名问题
- System.out.println 和 System.err.println 使用问题