目录
- 题目
- wait()、notify()和notifyAll()方法的特性和使用场景
- wait() 方法
- notify() 方法
- notifyAll() 方法
- 使用场景
- 注意事项
题目
选自牛客网
1.下面关于JAVA的垃圾回收机制,正确的是( )
A.当调用“System.gc()”来强制回收时,系统会立即回收垃圾
B.垃圾回收不能确定具体的回收时间
C.程序可明确地标识某个局部变量的引用不再被使用
D.程序可以显式地立即释放对象占有的内存
正确答案:B
正确的描述是选项B:“垃圾回收不能确定具体的回收时间”。Java的垃圾回收机制是自动运行的,它负责回收不再使用的对象所占用的内存。然而,垃圾回收的具体触发时间和频率是不确定的,由垃圾回收器根据系统的运行状态自动决定。即使调用了System.gc()
方法建议进行垃圾回收,也不能保证垃圾回收会立即执行,这只是一个建议,垃圾回收器可以选择忽略。因此,选项B是正确的。选项A和D的描述与Java垃圾回收机制的实际行为不符,而选项C描述的是可以通过将引用设为null
来暗示对象不再使用,但这并不是强制垃圾回收,垃圾回收器仍然会根据自己的策略来决定何时回收这些对象。
2.java中Hashtable, Vector, TreeSet, LinkedList哪些线程是安全的?
A.Hashtable
B.Vector
C.TreeSet
D.LinkedList
正确答案:AB
在Java中,
Hashtable
和Vector
是早期设计的集合类,它们内部的方法是同步的,因此它们是线程安全的。这意味着它们可以在多线程环境中共享而不需要额外的同步措施。然而,TreeSet
和LinkedList
并不提供内置的线程安全保障,它们的方法是非同步的,因此在多线程环境中共享时需要额外的同步控制,或者使用线程安全的包装类,如Collections.synchronizedSet()
和Collections.synchronizedList()
,来包装这些集合,以提供线程安全性。因此,正确答案是 A 和 B:
Hashtable
和Vector
是线程安全的集合类。
3.下面哪些写法能在 java8 中编译执行()
A.dir.listFiles((File f)->f.getName().endsWith(“.Java”));
B.dir.listFiles((File f)=>f.getName().endsWith(“.Java”));
C.dir.listFiles((_.getName().endsWith(“.Java”)));
D.dir.listFiles( f->f.getName().endsWith(“.Java”));
ad
形参列表:
- 形参列表定义了Lambda表达式接受的参数。参数类型可以被省略,Java编译器会根据上下文推断它们的类型。
- 如果Lambda表达式只有一个参数,那么甚至可以省略圆括号。例如,
(String s) -> s.length()
可以简化为String s -> s.length()
。箭头(→):
- 箭头是Lambda表达式的固定组成部分,用于分隔形参列表和代码块。它表示从参数到执行代码的转换。
代码块:
- 代码块包含了Lambda表达式执行的逻辑。如果代码块仅包含一条语句,那么可以省略花括号。
- 如果代码块中的语句是单一的返回语句,那么
return
关键字也可以被省略,Lambda表达式会自动返回这条语句的结果。基于这些规则,选项A和D的Lambda表达式是正确的,它们遵循了正确的语法和结构,能够编译执行。例如:
- 选项A:
dir.listFiles((File f) -> f.getName().endsWith(".Java"));
- 选项D:
dir.listFiles(f -> f.getName().endsWith(".Java"));
这两个选项中,Lambda表达式接受一个
File
类型的参数,并返回一个布尔值,表示文件名是否以".Java"结尾,这符合listFiles
方法需要的过滤器逻辑。
4.以下哪几种方式可用来实现线程间通知和唤醒:( )
A.Object.wait/notify/notifyAll
B.ReentrantLock.wait/notify/notifyAll
C.Condition.await/signal/signalAll
D.Thread.wait/notify/notifyAll
正确答案:AC
wait()、notify()和notifyAll()方法的特性和使用场景
wait()
、notify()
和notifyAll()
是Java中用于线程间通信的内置方法,它们定义在Object
类中,因此适用于所有Java对象。这些方法与同步机制紧密相关,它们必须在同步块或同步方法中被调用,以确保线程安全。
wait() 方法
wait()
方法允许一个线程放弃对象的锁,并等待直到另一个线程通知该对象锁已被释放。- 当一个线程调用对象的
wait()
方法时,它会立即释放该对象的锁,并进入到该对象的等待集合(wait set)中。 - 调用
wait()
方法必须在同步控制块或同步方法中进行,以避免违反锁的独占性。
notify() 方法
notify()
方法用于唤醒在同一个对象的等待集合中等待的单个线程。- 调用
notify()
方法的线程必须持有该对象的锁,但在调用后会立即释放锁,使得等待集合中的一个线程可以尝试重新获取锁。 - 被唤醒的线程将继续执行,但它能否成功获取锁取决于锁的可用性和其他线程的竞争。
notifyAll() 方法
notifyAll()
方法用于唤醒在同一个对象的等待集合中等待的所有线程。- 与
notify()
方法类似,调用notifyAll()
的线程必须持有该对象的锁,并在调用后释放锁。 - 所有等待集合中的线程都会被唤醒,但它们仍然需要竞争锁以继续执行。
使用场景
wait()
、notify()
和notifyAll()
通常用于实现生产者-消费者问题、读写锁、条件变量等多线程同步场景。- 这些方法可以帮助线程在某个条件尚未满足时暂停执行,并在条件满足时恢复执行,从而实现线程间的协作。
注意事项
- 在使用
wait()
、notify()
和notifyAll()
时,应当小心避免死锁和竞态条件。 - 通常建议在等待条件前使用循环检查来确认条件是否真的已经满足,以防止虚假唤醒(spurious wakeup)。
- 这些方法在多线程编程中是非常强大的工具,但也需要谨慎使用,以确保程序的正确性和性能。
3.以下代码输出的是:
public class SendValue{
public String str=“6”;
public static void main(String[] args) {
SendValue sv=new SendValue();
sv.change(sv.str);
System.out.println(sv.str);
}
public void change(String str) {
str=“10”;
}
}
A.6
B.10
C.都不对
D.16
正确答案:A
代码中的change
方法接受一个String
类型的参数str
,并将其修改为"10"
。然而,这个方法内部的str
变量是局部变量,它与类的成员变量str
是两个不同的引用。因此,即使局部变量str
的值被修改,类的成员变量str
的值仍然保持不变,其值为"6"
。所以,当打印出sv.str
的值时,输出结果是6
。这说明Java中字符串是不可变的,修改字符串实质上是创建了一个新的字符串对象,而不会改变原始字符串对象的值。因此,正确答案是A.6。