16.wait()和sleep()的区别?
问题:
在线程里 wait() 和 sleep() 的区别?
我的理解是执行 wait() 语句后,该线程仍是运行态,并且会占用CPU,但是执行 sleep()后,该线程则不会占用CPU,对吗?
为什么需要 sleep() 和 wait() 两条语句:他们底层是如何实现的?
回答:
线程 在wait 后,可以被另一个拥有相同 synchronized 对象的线程,通过调用 notify 唤醒,而 sleep 不行。wait 和 notify 能正常执行的条件是(否则会抛异常):多个线程的代码,都包在synchronized块中,并且 synchronized 锁的对象需要是同一个。如下所示:
Object mon = ...;
synchronized(mon) {
mon.wait();
}
上面这个线程调用了 wait后,会进入等待状态。这时另外一个线程可以这样做:
synchronized (mon) { mon.notify(); }
可以看到,synchronized锁对象,都是mon。因此,当第二个线程调用了 notify() 方法,第一个线程就会唤醒(假设有且仅有一个线程是被包在 synchronized (mon) 中且处于等待状态)。可以看到,synchronized锁对象,都是mon。因此,当第二个线程调用了 notify() 方法,第一个线程就会唤醒(假设有且仅有一个线程是被包在 synchronized (mon) 中且处于等待状态)。
如果有多个线程在等待(且synchronized 锁对象是同一个,如上例中的mon),则可以调用 notifyAll 来唤醒。但是,只有其中一个线程能抢到锁并继续执行(因为 wait 的线程都是在 synchronized 块内,需要争夺 synchronized 锁)。其他的线程会被锁住,直到他们依次获得锁。
再补充几点:
- wait 方法由 Object 对象调用(例如:你可以让 synchronized 锁对象调用 wait
,如上面例子的mon.wait()),而 sleep 则由线程调用。 - wait 之后,可能会伪唤醒(spurious wakeups)(正在waiting的线程,无故就被唤醒了,如遇到interrupted,
timing out等情况)。因此,你需要多设置一些检查,如果不满足实际的运行条件,则继续等待,如下:
synchronized {
while (!condition) {
mon.wait();
}
}
- 当线程调用 sleep 时,并没有释放对象锁,而 wait 则释放了对象锁:
synchronized(LOCK) {
Thread.sleep(1000); // LOCK is held
}
synchronized(LOCK) {
LOCK.wait(); // LOCK is not held
}
最后,再小结一下:
- sleep():“我已经完成了一个时间片,在n微秒前,请不要再给我一个时间片”。这时操作系统不会让这个线程做任何事情,直到sleep时间结束。
- wait():”我已经完成了一个时间片,在其他线程调用notify()前,请不要再给我一个时间片)。这时操作系统不会安排这个线程继续运行,直到有人调用了notify()
17.能否在一个构造器中调用另一个构造器
问题:能否在一个构造器中调用另一个构造器(在同一个类中,不是子类)?如果可以,怎么做? 调用另一个构造器的最好方法是什么(如果有几种方法可以选择的话)?
回答:
可以这样做:
public class Foo {
private int x;
public Foo() {
//Foo(1); //错误
this(1); // 这条语句必须位于第一句
}
public Foo(int x) {
this.x = x;
}
}
如果你想调用一个特定的父类构造器,而不是本类的构造器,应该使用super,而不是this. 请注意,在构造器中,你只能调用一次其他的构造器。并且调用其他构造器的语句,必须是这个构造器的第一个语句。
18.finally代码块是否总是会被执行?
问题:有一个 try/catch 代码块,其中包含一个打印语句。finally代码块总会被执行么?
示例:
try {
something();
return success;
}
catch (Exception e) {
return failure;
}
finally {
System.out.println("i don't know if this will get printed out.");
}
回答:
finally 将会被调用。 只有以下情况 finally 不会被调用:
- 当你使用 System.exit() 后(在try块或catch块中)
- 其他线程干扰了现在运行的线程(通过 interrupt 方法)
- JVM 崩溃( crash )了
class Test
{
public static void main(String args[])
{
System.out.println(Test.test());
}
public static int test()
{
try {
return 0;
}
finally {
System.out.println("finally trumps return.");
}
}
}
补充:
- 只有finally对应的try被执行了,finally才会被执行(但并不是一定会)。
在try-catch-finally中,当return遇到finally时,return对finally无效,即:
- 当try-catch里有return时,finally照样会执行。
- finally里的return会把try-catch里的return覆盖掉。
public static void main(String[] args) {
System.out.println(Test18.test());
}
public static String test() {
try {
System.out.println("try");
return test2();
} finally {
System.out.println("finally");
}
}
public static String test2() {
System.out.println("test2()");
return "I am test2()";
}
输出
try
test2()
finally
I am test2()
以上代码说明,finally块执行的时间是:try块的return语句执行之后,返回之前。
还是上面那段代码,在finally块后面加一个return语句,代码如下:
public static void main(String[] args) {
System.out.println(Test18.test());
}
public static String test() {
try {
System.out.println("try");
return test2();
} finally {
System.out.println("finally");
return "Hello";
}
}
public static String test2() {
System.out.println("test2()");
return "I am test2()";
}
输出:
try
test2()
finally
Hello
这说明:finally块的return语句会覆盖try块中的return语句。
19.如何将String转化为enum?
问题:
假设定义了如下的enum(枚举):
public enum Blah {
A, B, C, D
}
已知枚举对应的String值,希望得到对应的枚举值。例如,已知”A”,希望得到对应的枚举——Blah.A,应该怎么做?
Enum.valueOf()是否能实现以上目的,如果是,那我如何使用?
回答:
是的,Blah.valueOf(“A”) 将会得到 Blah.A
静态方法valueOf() 和 values() 不存在于源码中,而是在编译时创建,我们也可以在JavaDoc查看到它们,比如 Dialog.ModalityTyp 就中出现这两个方法。
public enum MyEnum {
A,B,C,D
}
public static void main(String[] args) {
System.out.println(Enum.valueOf(MyEnum.class, "A"));
}
输出
A
其他答案:
我有一个挺赞的工具方法:
/**
* A common method for all enums since they can't have another base class
* @param <T> Enum type
* @param c enum type. All enums must be all caps.
* @param string case insensitive
* @return corresponding enum, or null
*/
public static <T extends Enum<T>> T getEnumFromString(Class<T> c, String string) {
if( c != null && string != null ) {
try {
return Enum.valueOf(c, string.trim().toUpperCase());
} catch(IllegalArgumentException ex) {
}
}
return null;
}
你可以这么使用:
public static MyEnum fromString(String name) {
return getEnumFromString(MyEnum.class, name);
}
20.在Java中声明数组
问题描述: 你是如何在Java中声明数组的。
回答: 你可以直接用数组声明,或者通过数组的字面常量(array literal )声明
对于原始类型(primitive types):
int[] myIntArray = new int[3];
int[] myIntArray = {1, 2, 3};
int[] myIntArray = new int[]{1, 2, 3};
对于其他类,比如String类,也是相同的:
String[] myStringArray = new String[3];
String[] myStringArray = {"a", "b","c"};
String[] myStringArray = new String[]{"a", "b", "c"};