`
pjwqq
  • 浏览: 79747 次
社区版块
存档分类
最新评论

一道很有意思的java线程题

阅读更多

  

这几天看结城浩的《java多线程设计模式》,跟着做一些习题,有几道题目很有意思,记录下自己的体会。

  首先是题目(在原书212页,书尾有解答):

public class Main {
	public static void main(String[] args) {
		try {
			Blackhole.enter(new Object());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

 

public class Blackhole {	
        public static void enter(Object obj) throws InterruptedException {
		System.out.println("1");
		magic(obj);
		System.out.println("2");
		synchronized (obj) {
			System.out.println("3");
		}
	}

	private static void magic(Object obj){}
}

 代码很简单,要求写出magic()代码,使得输出是

 

1
2

 不能出现3.

 

 

  思路:很明显要想不输出3,magic()必须得到obj锁,且不能释放才行。我的答案:

 

	private static void magic(final Object obj) throws InterruptedException {
		new Thread() {
			@Override
			public void run() {
				synchronized (obj) {
					while (true) {
					}
				}
			}
		}.start();
		Thread.sleep(100);//略停片刻,确保新线程能先于主线程得到obj锁,  
                          //不加这句输出往往还是123  
                          //这题的难点就在这里  
	}

     显然sleep()用的很不专业吐舌头,那就来看作者的答案吧

 

	private static void magic(final Object obj) throws InterruptedException {
		Thread thread = new Thread() {
			@Override
			public void run() {
				synchronized (obj) {
					synchronized (this) {
						this.setName("LockNow");
						this.notifyAll();
					}
					while (true) {
					}
				}
			}
		};
		synchronized (thread) {
			thread.setName("");
			thread.start();
			while (thread.getName().equals("")) {
				thread.wait();
			}
		}
	}

   作者的思路很巧妙,通过thread.name的值来处理2个线程的执行次序。

   1. 创建一个内部Thread实例thread,先不start()

   2. 然后由主线程获得thread锁,并启动thread线程,然后开始等待。

   3. thread线程会去获得obj锁,获得obj锁之后,该线程会修改自己name,并通知主线程。

   4. 主线程发现条件满足,继续执行

 

   刚看到答案,有个很大疑问,主线程获得thread锁之后,启动thread线程,而thread线程为了修改name,必须获得自己的锁(否则运行时会报错java.lang.IllegalMonitorStateException),这不死锁了吗?

 

   仔细一想又不会,因为新线程开启之后,如果新线程运行到synchronized (this)被阻挡而无法修改name,主线程肯定会进入wait,而wait时主线程释放thread锁,新线程就可继续往下跑。

  短短几行代码,线程之间同步协调环环相扣,不得不佩服作者的功力!

  

  

  

 

2
1
分享到:
评论
20 楼 msdghs 2016-11-07  
kidding87 写道
涂简单,下面没有更短的了把
private static void magic(Object obj){
			System.out.println(2);
			System.exit(0);
		}



这楼就服你
19 楼 pjwqq 2014-09-01  
bit1129 写道
中国人的书就在这些诡谲伎俩上下功夫,

日本的,兄弟
18 楼 bit1129 2014-08-30  
中国人的书就在这些诡谲伎俩上下功夫,
17 楼 mahongming 2014-06-27  
再来一发
public static void magic(final Object lock) {
		Thread t = new Thread() {
			@Override
			public void run() {
				synchronized (lock) {
					while (true) {
					}
				}
			}
		};
		t.start();
		Thread.State s = t.getState();
		while (!s.equals(s.RUNNABLE)) {
			s = t.getState();
		}
	}

16 楼 mahongming 2014-06-27  
哪里有问题,这有什么关系,在它之前执行的话为flag=-1就进while继续等,直到值被修改为0之后才跳出while,此时锁已经被thread拿跑了,在它之后执行flag=0都不用进while循环了,说明锁已经被新开的thread成功拿到。
kidding87 写道
有问题把,你不能保证 change.get()一定在 change.incrementAndGet();之前执行
mahongming 写道
重新排下版
public static void magic(final Object lock) {
		final AtomicInteger change = new AtomicInteger(-1);
		new Thread() {
			@Override
			public void run() {
				synchronized (lock) {
					change.incrementAndGet();
					while (true) {
					}
				}
			}
		}.start();
		int flag = change.get();
		while (flag < 0) {
			flag = change.get();
		}

	}




15 楼 kidding87 2014-06-27  
有问题把,你不能保证 change.get()一定在 change.incrementAndGet();之前执行
mahongming 写道
重新排下版
public static void magic(final Object lock) {
		final AtomicInteger change = new AtomicInteger(-1);
		new Thread() {
			@Override
			public void run() {
				synchronized (lock) {
					change.incrementAndGet();
					while (true) {
					}
				}
			}
		}.start();
		int flag = change.get();
		while (flag < 0) {
			flag = change.get();
		}

	}



14 楼 mahongming 2014-06-27  
重新排下版
public static void magic(final Object lock) {
		final AtomicInteger change = new AtomicInteger(-1);
		new Thread() {
			@Override
			public void run() {
				synchronized (lock) {
					change.incrementAndGet();
					while (true) {
					}
				}
			}
		}.start();
		int flag = change.get();
		while (flag < 0) {
			flag = change.get();
		}

	}


13 楼 mahongming 2014-06-27  
我艹,我也来一个

public static void magic(final Object lock) {
final AtomicInteger change = new AtomicInteger(-1);
new Thread() {
@Override
public void run() {
synchronized (lock) {
change.incrementAndGet();
while (true) {
}
}
}
}.start();
int flag = change.get();
while (flag < 0) {
flag = change.get();
}

}
12 楼 pjwqq 2014-06-26  
10楼的解法不错,赞一个
11 楼 pjwqq 2014-06-26  
kevinrao0101 写道
仔细一想又不会,因为新线程开启之后,如果新线程运行到synchronized (this)被阻挡而无法修改name,主线程肯定会进入wait,而wait时主线程释放thread锁,新线程就可继续往下跑。

这句话有些无法理解,为什么thread在拿不到自己的锁的时候,主线程就会wait?
thread.wait();这句话不是应该thread找个线程进入等待状态吗?但是按照执行的结果来看,貌似是主线程进入等待状态了

thread拿不到自己的锁,就不能修改name,根据条件主线程就会wait
thread.wait() 意思是当前持有thread锁的线程-就是主线程进入wait
10 楼 kidding87 2014-06-26  
刚贴的有点漏洞,这个就没有啦
private static void magic(final Object obj){
			final Object c = new Object();
			Thread t = new Thread(){
				@Override
				public void run() {
						try {
							synchronized(obj){
								synchronized(c){
									c.notify();
								}
								Thread.sleep(5000);
								//5秒玩够了就自动退出好了要不还要手动点stop
								System.exit(0);
							}
						} catch (Exception e) {
						}
					
				}
			};
			synchronized (c) {
				try {
					t.start();
					c.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}


kidding87 写道
多线程就用多线程的玩法
private static void magic(final Object obj){
			final Object c = new Object();
			Thread t = new Thread(){
				@Override
				public void run() {
						try {
							synchronized(c){
								c.notify();
							}
							synchronized(obj){
								Thread.sleep(5000);
								//5秒玩够了就自动退出好了要不还要手动点stop
								System.exit(0);
							}
						} catch (Exception e) {
						}
					
				}
			};
			synchronized (c) {
				try {
					t.start();
					c.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

9 楼 kidding87 2014-06-26  
多线程就用多线程的玩法
private static void magic(final Object obj){
			final Object c = new Object();
			Thread t = new Thread(){
				@Override
				public void run() {
						try {
							synchronized(c){
								c.notify();
							}
							synchronized(obj){
								Thread.sleep(5000);
								//5秒玩够了就自动退出好了要不还要手动点stop
								System.exit(0);
							}
						} catch (Exception e) {
						}
					
				}
			};
			synchronized (c) {
				try {
					t.start();
					c.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

8 楼 kidding87 2014-06-26  
涂简单,下面没有更短的了把
private static void magic(Object obj){
			System.out.println(2);
			System.exit(0);
		}

7 楼 kidding87 2014-06-26  
感觉线程用起来没有反射厉害
private static void magic(Object obj){
			PrintStream origin = System.out;
			origin.println(2);
			try {
				Field f =  System.class.getDeclaredField("out");
				/*去除final修饰符的影响,将字段设为可修改的*/  
				f.setAccessible(true);
				Field modifiersField = Field.class.getDeclaredField("modifiers");  
				modifiersField.setAccessible(true);  
				modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);  
				f.set(System.class, new PrintStream(new ByteArrayOutputStream()));
			} catch (Exception e){
				e.printStackTrace();
			}
		}
6 楼 kevinrao0101 2014-06-26  
仔细一想又不会,因为新线程开启之后,如果新线程运行到synchronized (this)被阻挡而无法修改name,主线程肯定会进入wait,而wait时主线程释放thread锁,新线程就可继续往下跑。

这句话有些无法理解,为什么thread在拿不到自己的锁的时候,主线程就会wait?
thread.wait();这句话不是应该thread找个线程进入等待状态吗?但是按照执行的结果来看,貌似是主线程进入等待状态了
5 楼 pjwqq 2014-06-26  
gufengyy 写道
lishidi 写道
    private static void magic(final  Object obj) throws InterruptedException{
    final CountDownLatch latch = new CountDownLatch(1);
    Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
latch.countDown();
while(true){

}
}

}
});
   
    thread.start();
    latch.await();
   
    }
多个锁比较啰嗦,稍微简单一点的实现,仅作参考


三楼的实现方法可以达到同样的效果,但个人感觉原书的作者是不想使用JDK的并发类库吧。

作者写书时还是jdk1.4,CountDownLatch是1.5的
4 楼 gufengyy 2014-06-26  
lishidi 写道
    private static void magic(final  Object obj) throws InterruptedException{
    final CountDownLatch latch = new CountDownLatch(1);
    Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
latch.countDown();
while(true){

}
}

}
});
   
    thread.start();
    latch.await();
   
    }
多个锁比较啰嗦,稍微简单一点的实现,仅作参考


三楼的实现方法可以达到同样的效果,但个人感觉原书的作者是不想使用JDK的并发类库吧。
3 楼 lishidi 2014-06-26  
    private static void magic(final  Object obj) throws InterruptedException{
    final CountDownLatch latch = new CountDownLatch(1);
    Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
latch.countDown();
while(true){

}
}

}
});
   
    thread.start();
    latch.await();
   
    }
多个锁比较啰嗦,稍微简单一点的实现,仅作参考
2 楼 pjwqq 2014-06-26  
永志_爱戴 写道
实现的过于啰嗦了

能给出你的实现吗?学习下
1 楼 永志_爱戴 2014-06-26  
实现的过于啰嗦了

相关推荐

    线程 JAVA java线程 java线程第3版 java线程第2版第3版合集

    在Java出现以前,似乎人人都在谈论线程,却很少有人使用它。用线程编程是技巧性很强的且不可移植。 而在Java中却完全不同。Java的线程工具易于使用,并且像Java中的其他东西一样可以在不同的平台之间移植。这是一件...

    Java线程讲解Java线程讲解

    Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解

    Java线程详解大全

    Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程

    Java线程Java线程Java线程Java线程

    Java线程Java线程Java线程Java线程Java线程Java线程

    java多线程编程总结

    Java线程:概念与原理 Java线程:创建与启动 Java线程:线程栈模型与线程的变量 Java线程:线程状态的转换 Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:线程的调度-优先级 ...

    15个Java线程并发面试题和答案.docx

    15个Java线程并发面试题和答案.docx,15个Java线程并发面试题和答案.docx 15个Java线程并发面试题和答案.docx,15个Java线程并发面试题和答案.docx

    Java多线程习题Java多线程习题.doc

    Java多线程习题Java多线程习题

    JAVA线程练习题及答案.pdf

    JAVA线程练习题及答案.pdf

    java基础多线程练习题(1)

    java基础之多线程的练习题,博客访问地址: http://blog.csdn.net/u014028392/article/details/76906801

    Java多线程编程总结

    Java 线程系列博文总结word化,编目如下,欢迎互相学习交流: Java线程:概念与原理 Java线程:创建与启动 Java线程:线程栈模型与线程的变量 Java线程:线程状态的转换 Java线程:线程的同步与锁 Java线程:...

    java 线程java 线程

    java 线程java 线程java 线程java 线程java 线程java 线程java 线程java 线程java 线程

    Java线程面试题Top50[参照].pdf

    Java线程面试题Top50[参照].pdf

    Java线程模块Java线程之秒表

    Java线程模块Java线程之秒表新手学习Java线程模块时,利用Java中设置线程的暂停间隔,做的简易秒表

    java线程 线程学习资料 java线程教程

    java线程 线程 教程 java线程教程 java线程学习资料 本教程有什么内容? 本教程研究了线程的基础知识— 线程是什么、线程为什么有用以及怎么开始编写使用线程的简单 程序。 我们还将研究更复杂的、使用线程的应用...

    java 线程 dump 分析工具 2.3.3

    java 线程Dump 分析工具: Java的TDA线程转储分析器是一个用于分析Sun Java VM生成的线程转储和堆信息的小型Swing GUI(目前用1.4测试)。它从提供的日志文件中解析线程转储和类直方图。它提供关于发现的线程转储的...

    Java多线程练习题

    这是对Java多线程的一些相关练习题 包括选择 填空 简答 编程等题型

    java多线程笔记

    Java线程:概念与原理 2 一、操作系统中线程和进程的概念 2 二、Java中的线程 3 三、Java中关于线程的名词解释 3 四、线程的状态转换和生命周期 4 Java线程:创建与启动 7 Java线程:线程名称的设定及获取 10 Java...

    java线程同步java线程同步

    java线程同步java线程同步java线程同步

    JAVA多线程练习题答案。

    JAVA多线程练习题的答案。都是一些基本的练习题。对初学者有帮助

    java线程分析工具TDA

    分析java线程日志的工具,使用jstack把java线程日志dump下来,然后上传到该工具,就可以查看线程阻塞情况等信息。

Global site tag (gtag.js) - Google Analytics