29. Java中的wait()和sleep()方法之间有什么区别?
等待():
- wait()方法释放锁定。
- wait()是Object类的方法。
- wait()是非静态方法–公共最终void wait()引发InterruptedException {//…}
- 应该通过notify()或notifyAll()方法通知wait()。
- 需要从循环中调用wait()方法以处理错误警报。
- 必须从同步上下文(即同步方法或块)中调用wait()方法,否则它将引发IllegalMonitorStateException
sleep():
- sleep()方法不会释放锁。
- sleep()是java.lang.Thread类的方法。
- sleep()是静态方法–公共静态无效睡眠(长毫秒,int nanos)抛出InterruptedException {//…}
- 在指定的时间后,sleep()完成。
- sleep()最好不要从循环中调用(即参见下面的代码)。
- sleep()可以从任何地方调用。没有具体要求。
30.当未捕获的异常离开run()
方法时会发生什么?
我可能碰巧一个未经检查的异常从run()
方法中逃逸了。在这种情况下,线程由Java虚拟机停止。通过将实现接口的实例注册UncaughtExceptionHandler
为异常处理程序,可以捕获此异常。
这可以通过调用static方法来完成,该方法Thread.setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler)
告诉JVM在线程自身上没有注册任何特定的处理程序的情况下使用提供的处理程序,或者通过setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler)
在线程实例本身上进行调用。
31.两个接口Runnable
和
Callable?
接口Runnable
定义了run()
没有任何返回值Callable
的方法call()
,而接口则允许方法返回值并抛出异常。
32.什么是关机钩?
关闭钩子是在JVM关闭时执行的线程。可以通过addShutdownHook(Runnable)
在Runtime实例上调用来注册它:
Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { } });
2.2通过sleep暂停执行
33.如何防止繁忙的等待?
防止繁忙等待的一种方法是使当前线程在给定的时间内休眠。这可以java.lang.Thread.sleep(long)
通过传递方法将进入睡眠的毫秒数作为参数来调用该方法来完成。
34.我们可以Thread.sleep()
用于实时处理吗?
传递给的毫秒数Thread.sleep(long)
仅表示调度程序不需要当前线程执行多长时间。根据实际的实现,调度程序可能会让线程提前或延迟几毫秒再执行一次。因此,Thread.sleep()
不应将的调用用于实时处理。
35.该方法Thread.yield()
做什么?
静态方法的调用为Thread.yield()
调度程序提供了一个提示,即当前线程愿意释放处理器。调度程序可以随意忽略此提示。由于未定义调用哪个线程将获得处理器Thread.yield()
,因此甚至有可能当前线程成为要执行的“下一个”线程。
36.该类有哪些用例java.util.concurrent.Future
?
该类的实例java.util.concurrent.Future
用于表示异步计算的结果,这些结果无法立即获得。因此,该类提供了一些方法来检查异步计算是否已完成,取消任务并检索实际结果。后者可以通过提供的两种get()
方法来完成。第一个get()
方法不带参数,并阻塞直到结果可用为止;第二个get()
方法带一个超时参数,如果结果在给定的时间范围内不可用,则该超时参数使方法调用返回。
37.我们如何在Java中停止线程?
要停止线程,可以使用指向当前线程的易失性引用,该引用可以被其他线程设置为null,以指示当前线程应停止其执行:
private static class MyStopThread extends Thread { private volatile Thread stopIndicator; public void start() { stopIndicator = new Thread(this); stopIndicator.start(); } public void stopThread() { stopIndicator = null; } @Override public void run() { Thread thisThread = Thread.currentThread(); while(thisThread == stopIndicator) { try { Thread.sleep(1000); } catch (InterruptedException e) { } } } }
2.3中断
38.如何唤醒使用前已进入睡眠状态的线程Thread.sleep()
?
该方法interrupt()
的java.lang.Thread
中断睡眠线程。已通过调用进入睡眠状态的中断线程Thread.sleep()
被唤醒InterruptedException
:
public class InterruptExample implements Runnable { public void run() { try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException e) { System.out.println("["+Thread.currentThread().getName()+"] Interrupted by exception!"); } } public static void main(String[] args) throws InterruptedException { Thread myThread = new Thread(new InterruptExample(), "myThread"); myThread.start(); System.out.println("["+Thread.currentThread().getName()+"] Sleeping in main thread for 5s..."); Thread.sleep(5000); System.out.println("["+Thread.currentThread().getName()+"] Interrupting myThread"); myThread.interrupt(); } }
39.线程如何查询是否已被中断?
如果线程不在Thread.sleep()
抛出的方法之内InterruptedException
,则该线程可以通过调用静态方法Thread.interrupted()
或isInterrupted()
从其继承的方法来查询是否已被中断。java.lang.Thread.
40.应该如何InterruptedException
处理?
像sleep()
和join()
抛出这样的方法InterruptedException
告诉调用者另一个线程已中断该线程。在大多数情况下,这样做是为了告诉当前线程停止其当前计算并意外完成它们。因此,通过捕获异常并仅将其记录到控制台或某些日志文件中来忽略该异常通常不是处理此类异常的适当方法。此异常的问题在于,run()
Runnable接口的方法不允许run()
抛出任何异常。因此,仅将其重新添加无济于事。这意味着的实现run()
必须自己处理此检查的异常,这通常会导致其被捕获和忽略的事实。
2.4加入
41.在启动子线程之后,我们如何在父线程中等待子线程的终止?
通过调用join()
线程的实例变量上的方法来等待线程终止:
Thread thread = new Thread(new Runnable() { @Override public void run() { } }); thread.start(); thread.join();
42.以下程序的输出是什么?
上面的代码的输出为“ false”。尽管MyDaemonThread的实例是守护程序线程,但是的调用join()
会使主线程等待,直到守护程序线程的执行完成。因此,调用isAlive()
线程实例显示守护程序线程不再运行。
3.同步
43.同步方法和同步块之间的区别?
- 同步块减小了锁定范围,但是同步方法的锁定范围是整个方法。
- 同步块的性能更好,因为只有关键部分被锁定,但是同步方法的性能比块差。
- 同步块提供了对锁的精细控制,但对此对象或类级别锁表示的当前对象提供了同步方法锁。
- 同步块可以抛出NullPointerException,但同步方法不会抛出。
- 同步块:同步(此){}
- 同步方法:公共同步void fun(){}
44.什么是Java中的volatile关键字,它与Java中的同步方法有何不同?
使用volatile强制线程直接从RAM存储器读取和写入变量。因此,当许多线程都使用相同的volatile变量时,它们都将看到RAM内存中存在的最新版本,而不是高速缓存中可能的旧副本。当线程进入同步块时,它需要控制监视变量。所有其他线程等待,直到第一个线程从同步块退出。为了确保所有线程都能看到相同的修改,同步块中使用的所有变量都直接从RAM内存而不是从缓存副本读取和写入。
45.同步关键字用于什么目的?
当您必须实现对资源的互斥访问(例如某些静态值或某些文件引用)时,可以使用同步块包含与互斥资源一起使用的代码:
synchronized (SynchronizedCounter.class) { counter++; }
46.什么是信号量?
信号量是一种数据结构,它维护一组必须由竞争线程获取的许可。因此,信号量可用于控制有多少线程同时访问关键部分或资源。因此,的构造函数java.util.concurrent.Semaphore
将线程竞争的许可数量作为第一个参数。每次调用其acquire()
方法都会尝试获取可用许可之一。acquire()
在下一个允许获得可用之前,没有任何参数块的方法。稍后,当线程完成对关键资源的工作时,它可以通过release()
在Semaphore实例上调用该方法来释放许可。
47.什么是CountDownLatch
?
SDK类CountDownLatch
提供了一个同步辅助工具,可用于实现以下场景:线程必须等待,直到其他一些线程达到相同状态才能启动所有线程。通过提供一个递减的同步计数器,直到其达到零值,即可完成此操作。CountDownLatch
实例达到零后,将允许所有线程继续进行。可以使用计数器的值1来让所有线程在给定的时间点启动,也可以等待直到多个线程完成。在后一种情况下,计数器将使用线程数进行初始化,并且每个完成其工作的线程都会将锁存器递减一个。
48. aCountDownLatch
和a有CyclicBarrier
什么区别?
这两个SDK类都在内部维护一个计数器,该计数器由不同的线程递减。线程等待直到内部计数器达到零值,然后从那里继续。但是与CountDownLatch
类相反,该类CyclicBarrier
将值重置为零后将内部值重置为初始值。顾名思义,CyclicBarrier
因此的实例可用于实现用例,其中线程必须一次又一次地等待彼此。
3.1内部锁和同步
49.同步方法获得什么内在锁?
同步方法获取该方法对象的固有锁定,并在方法返回时释放该锁定。即使该方法引发异常,也将释放固有锁定。因此,同步方法等于以下代码:
public void method() { synchronized(this) { ... } }
50.如果两个线程同时在不同的对象实例上调用同步方法,这些线程之一会阻塞吗?
两种方法都锁定同一监视器。因此,您不能同时在不同线程上的同一对象上执行它们(两种方法之一将阻塞,直到另一种方法完成)。
51.构造函数可以同步吗?
否,构造函数无法同步。之所以导致语法错误,是因为只有构造线程才有权访问正在构造的对象。
52. Lock与同步相比有什么好处?
锁的优点是:
- 有可能使它们公平
- 在等待Lock对象时,可以使线程响应中断。
- 可以尝试获取锁,但是如果无法获取锁,则立即返回或在超时后返回
- 可以在不同的范围内以不同的顺序获取和释放锁
53.原始值可以用于内部锁吗?
不可以,原始值不能用于内部锁。
54.内在锁是否可重入?
是的,同一线程可以一次又一次地访问固有锁。否则,获取锁的代码将必须注意,它不会偶然尝试获取已获取的锁。
55.我们对公平锁有什么了解?
选择是通过障碍的一些独家资源的下一个线程时,一个公平的锁占用线程的等待时间考虑在内。公平锁的示例实现由Java SDK提供:java.util.concurrent.locks.ReentrantLock
。如果使用布尔标志设置为true的构造函数,则ReentrantLock授予对等待时间最长的线程的访问权限。
56. SDK类ReadWriteLock使用了哪种减少锁争用的技术?
SDK类ReadWriteLock
使用以下事实:并发线程在没有其他线程尝试更新值的情况下想要读取值时不必获取锁。这由一对锁实现,一个锁用于只读操作,一个锁用于写操作。尽管可以通过多个线程获得只读锁,但是该实现保证释放写锁后,所有读取操作都将看到更新的值。
3.2原子访问
57.我们通过原子操作了解什么?
原子操作是完全执行或根本不执行的操作。
58.语句c ++是原子的吗?
不,一个整数变量的增量包括一个以上的运算。首先,我们必须加载c的当前值,将其递增,然后最后将新值存储回去。执行此增量的当前线程可能会在这三个步骤中的任何一个之间中断,因此此操作不是原子操作。
59. Java中哪些操作是原子操作?
Java语言提供了一些原子性的基本操作,因此可用于确保并发线程始终看到相同的值:
- 对参考变量和原始变量(长整型和双精度型除外)的读写操作
- 对声明为易失性的所有变量的读写操作
4.活泼
4.1死锁
60.我们对僵局有什么了解?
死锁是一种情况,其中两个(或更多)线程各自在另一个线程上等待以释放其已锁定的资源,而线程本身已锁定另一个线程在等待的资源:
- 线程1:锁定资源A,等待资源B
- 线程2:锁定资源B,等待资源A
61.僵局情况有哪些要求?
通常,可以确定以下死锁要求:
- 互斥:有一种资源在任何时间点只能由一个线程访问。
- 资源持有:锁定一个资源后,线程尝试获取对某个其他排他资源的另一个锁定。
- 无抢占:没有机制,如果一个线程在特定时间段内持有锁,则该机制可以释放资源。
- 循环等待:在运行时发生一个星座,其中两个(或更多)线程分别在另一个线程上等待以释放已锁定的资源。
62.完全可以防止死锁吗?
为了防止死锁,必须消除一个或多个死锁的要求:
- 互斥:在某些情况下,可以通过使用乐观锁定来防止互斥。
- 资源持有:线程无法成功获取所有排他锁时,可能会释放其所有排他锁。
- 无抢占:对独占锁使用超时将在给定时间后释放锁。
- 循环等待:当所有线程以相同顺序获得所有排他锁时,不会发生循环等待。
63.是否可以实现死锁检测?
当监视所有排他锁并将其建模为有向图时,死锁检测系统可以搜索两个线程,每个线程都在等待另一个线程以释放已锁定的资源。然后可以通过某种异常强制等待线程释放另一个线程正在等待的锁。
4.2饥饿和活锁
64.什么是活锁?
活锁是这样的情况,其中两个或更多线程通过响应另一个线程引起的动作而相互阻塞。与死锁情况相反,死锁情况是两个或多个线程在一个特定状态下等待,而参与活动锁的线程则以防止其正常工作进展的方式更改其状态。一个示例是这样一种情况,其中两个线程尝试获取两个锁,但是在无法获取第二个锁时释放它们获取的锁。现在可能会发生,两个线程同时尝试获取第一个线程。由于只有一个线程成功,因此第二线程可以成功获取第二锁。现在,两个线程都持有两个不同的锁,但是由于两个线程都想要拥有两个锁,因此它们释放其锁并从头开始尝试。现在可能一次又一次地发生这种情况。
65.通过线程饥饿我们了解什么?
具有较低优先级的线程比具有较高优先级的线程获得的执行时间更少。当具有较低优先级的线程执行长时间的持久计算时,可能会发生这些线程没有足够的时间及时完成其计算的情况。它们似乎“饿死了”,因为具有更高优先级的线程会占用它们的计算时间。