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无效,即:

  1. 当try-catch里有return时,finally照样会执行。
  2. 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"};