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

一道很有意思的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基础多线程练习题(1)

    总结来说,Java基础多线程练习题主要涵盖了线程的创建、同步与通信、线程安全以及并发工具的使用。通过这些题目,你可以更好地理解线程的工作原理,学会在实际项目中有效利用多线程提高程序性能,避免潜在的问题。在...

    JAVA多线程练习题答案。

    JAVA多线程练习题答案详解 在本文中,我们将对 JAVA 多线程练习题的答案进行详细的解释和分析。这些题目涵盖了 JAVA 多线程编程的基本概念和技术,包括线程的生命周期、线程同步、线程状态、线程优先级、线程安全等...

    java 线程工具类 java 线程工具类

    java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具...

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

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

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

    Java 线程面试题 Top 50 Java 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。 一、什么是...

    JAVA线程练习题及答案.pdf

    JAVA线程练习题及答案.pdf

    Java多线程练习题

    Java多线程是Java编程中的核心概念,它允许程序同时执行多个任务,提高了系统的效率和响应性。在Java中,多线程的实现主要通过两种方式:继承Thread类和实现Runnable接口。理解并掌握多线程的使用对于任何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线程分析工具TDA

    Java线程分析是Java开发中的重要环节,尤其是在处理性能优化、死锁排查或者并发问题时。TDA(Thread Dump Analyzer)是一款强大的Java线程分析工具,它能够帮助开发者深入理解应用在运行时的线程状态,包括线程的...

    java 线程相关工具类

    java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类....

    Java工程师线程试题

    Java线程是并发编程的重要组成部分,它允许程序在同一时间执行多个任务。在Java中,线程的使用可以提高系统的执行效率,减少处理器的空转时间和上下文切换的时间,但并不一定能提高程序代码的可读性。以下是根据题目...

    JAVA线程dump的分析

    JAVA线程dump的分析 JAVA线程dump是指在JAVA程序中,当前线程的状态和调用堆栈的快照,能够帮助开发者了解当前程序的执行情况,诊断问题和性能瓶颈。生成JAVA线程dump的方法在不同的操作系统下是不同的,在Windows...

    java多线程Demo

    Java多线程是Java编程中的一个重要概念,它允许程序同时执行多个任务,提高了程序的效率和响应速度。在Java中,实现多线程有两种主要方式:继承Thread类和实现Runnable接口。 1. 继承Thread类: 当我们创建一个新...

    JAVA线程学习(源代码)

    在Java编程语言中,线程是程序执行的基本单元,它允许程序同时执行多个任务。Java提供了丰富的线程API,使得开发者能够轻松地创建、管理和控制线程。本资源"JAVA线程学习(源代码)"提供了关于Java线程的源代码示例,...

    Java多线程设计模式上传文件

    Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...

Global site tag (gtag.js) - Google Analytics