返回信息流Java 高并发编程详解 第64页 关于Synchronized的使用 示例程序,这个程序逻辑有问题吧,每次运行只有一个线程能打印所有数据,其他两个线程得到锁的时候,都打印完了····根本不是并行打印。
public class TicketWindowRunnable implements Runnable{
private int index = 1 ;
private final static int MAX = 500;
private final static Object Mutex = new Object();
public void run() {
synchronized(Mutex) {
while(index<=MAX) {
System.out.println(Thread.currentThread()+" 的号码是" + index++);
}
}}
public static void main(String[] args) {
final TicketWindowRunnable task = new TicketWindowRunnable();
Thread windowThread1 = new Thread(task,"一号窗口");
Thread windowThread2 = new Thread(task,"二号窗口");
Thread windowThread3 = new Thread(task,"三号窗口");
windowThread1.start();
windowThread2.start();
windowThread3.start();
}
}
这是一条镜像帖。来源:北邮人论坛 / java / #60540同步于 2018/11/30
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Java机器人发帖
这个程序有问题吧?读书看到的
zidian321
2018/11/30镜像同步8 回复
订阅后,新回复会通过你的通知中心匿名送达。
8 条回复
许多书籍都有错,看明白就OK。不确定的话,实践一下,尤其在初学阶段
【 在 zidian321 (tomato) 的大作中提到: 】
: Java 高并发编程详解 第64页 关于Synchronized的使用 示例程序,这个程序逻辑有问题吧,每次运行只有一个线程能打印所有数据,其他两个线程得到锁的时候,都打印完了····根本不是并行打印。
: public class TicketWindowRunnable implements Runnable{
: private int index = 1 ;
: ...................
我的理解:确实是会有一个线程一直获得锁,直到数组遍历打印结束(都在一个线程中执行完成),其实并不是程序执行过快导致的,就是因为其他线程同步代码块没有获得锁,所以不能打印。原文里也只是说,加上关键字避免了多线程处理数据不一致的问题,所以也没说错。
1. 如果你想要让多个线程都能打印,就要线程每打印一次,跳出同步代码块,让其他线程也能获得锁。
2. 如果你想让结果更明确一些,比如不要让一个线程连续打印,就可以加个sleep,让当前线程继续执行同步块之外的代码,好让剩余的其他两个线程去竞争锁。
package TicketWindowRunnable;
public class TicketWindowRunnable implements Runnable{
private int index = 1 ;
private final static int MAX = 500;
private final static Object Mutex = new Object();
public void run() {
while(true) {
try {
out:
synchronized (Mutex) {
while (index <= MAX) {
System.out.println(Thread.currentThread() + " 的号码是" + index++);
break out;
}
}
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final TicketWindowRunnable task = new TicketWindowRunnable();
Thread windowThread1 = new Thread(task,"一号窗口");
Thread windowThread2 = new Thread(task,"二号窗口");
Thread windowThread3 = new Thread(task,"三号窗口");
windowThread1.start();
windowThread2.start();
windowThread3.start();
}
}
因为是一个叫号的程序,所以叫号之后你加上个“处理业务的时间”,就能实现“同一个窗口”,不会“连续叫号”的情况。
附上结果:
因为syncronized是非公平锁,当你一个任务运行特别快的时候是可能发生这种情况的。
而且就是如果你运行一次的时间小于切换上下文的时间,那这个场景就不适合并发。
可以去了解一下锁底层的优化,偏向锁,轻量级锁
没看过这本书,看代码,synchronized代码块包裹了while,然后代码的整个逻辑就是while代码块,所以这么写其实就一个任务,然后分配到了一个线程,然后跑完while就执行完了,释放锁,没毛病。其它线程因为没有获得锁所以都饿死了,等第一个线程跑完释放锁,后面的线程获取锁后跑到while这里一看发现“活都干完了啊,好像没我什么事,一脸懵逼”。多线程卖票的话同步代码块应该放到while循环里面才对,这样线程们才又机会去竞争。我是这么理解的。另外synchronized释放锁一般在1.当前线程同步代码块执行结束(你贴的代码这里就是整个while部分);2.当前线程遇到break 、 return ;3.当前线程出现未处理的error或者exception导致异常结束;4.当前线程程序执行了同步对象wait方法 ,当前线程暂停,释放锁;另外像当前线程程序调用 Thread.sleep()、Thread.yield() 这些方法能暂停线程的执行,但是并不会释放锁,所以你贴的这代码想靠sleep“看效果”是没用的。
当然,从细节看,把synchronize提到while里面后还有一个细节,你要保证不“多卖票”的话还需要在输出那判断一下if(index<=MAX),不然很可能就多卖两张票了。。。。。
【 在 bearsmall 的大作中提到: 】
: 没看过这本书,看代码,synchronized代码块包裹了while,然后代码的整个逻辑就是while代码块,所以这么写其实就一个任务,然后分配到了一个线程,然后跑完while就执行完了,释放锁,没毛病。其它线程因为没有获得锁所以都饿死了,等第一个线程跑完释放锁,后面的线程获取锁后跑到while这里一看发现“活都干完了啊,好像没我什么事,一脸懵逼”。多线程卖票的话同步代码块应该放到while循环里面才对,这样线程们才又机会去竞争。我是这么理解的。另外synchronized释放锁一般在1.当前线程同步代码块执行结束(你贴的代码这里就是整个while部分);2.当前线程遇到break 、 return ;3.当前线程出现未处理的error或者exception导致异常结束;4.当前线程程序执行了同步对象wait方法 ,当前线程暂停,释放锁;另外像当前线程程序调用 Thread.sleep()、Thread.yield() 这些方法能暂停线程的执行,但是并不会释放锁,所以你贴的这代码想靠sleep“看效果”是没用的。