不懂并发?看完就超神!!!Java并发基础(六)

并发面试题

sleep和wait、yield的异同

wait 方法是属于 Object 类中的,wait 过程中线程会释放对象锁,只有当其他线程调用 notify 才能唤醒此线程。wait 使用时必须先获取对象锁,所以在调用wait()时,该线程必须已经获得锁即必须在 synchronized 修饰的代码块中使用,那么相应的 notify 方法同样必须在 synchronized 修饰的代码块中使用,如果没有在synchronized 修饰的代码块中使用时运行时会抛出IllegalMonitorStateException的异常。

sleep 方法是属于 Thread 类中的,sleep 过程中线程不会释放锁,只会阻塞线程,让出cpu给其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态,可中断,sleep 给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会。

yield和 sleep 一样都是 Thread 类的方法,都是暂停当前正在执行的线程对象,不会释放资源锁,和 sleep 不同的是 yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。还有一点和 sleep 不同的是 yield 方法只能使同优先级或更高优先级的线程有执行的机会。

为什么wait()和notify()需要搭配synchonized关键字使用

答:使用wait会强迫线程放弃锁,前提是必须获取到了锁,如果没有锁而调用wait会抛出异常,同理notify。

volatile原理

JMM中主要分为主存和每个线程自己的工作内存,当一个线程频繁读取一个共享变量时,JIT会将这个变量缓存到线程的工作内存中,以提高效率,但是在多线程下,当有另外一个线程将变量改变,当前这个线程任然会读取自己工作内存中的缓存值,这样就导致数据不一致。

而volatile底层使用内存屏障,其细分为读屏障,写屏障,当对变量进行写操作时,会在写操作后加上写屏障,写屏障前的改动都会同步到主存中,进行读操作时会在读操作前加上读屏障,读屏障后的读操作都是读取主存中最新的数据。同时读写屏障还可以保证有序性,读屏障后的操作不会被重排到前面,写屏障前的操作不会到后面。但是volatile无法保证原子性。

synchronized原理

synchronized是重量锁,底层使用Monitor,每个对象头的markl word字段表示使用heavyweightlock都可以关联一个Monitor,Monitor中有owner,entryset,waitset,owner表示谁拥有这个锁,entryset就是竞争锁失败后进入的阻塞队列,waitset是线程成为owner然后调用wait方法进入的等待队列,使用notify可以将其唤醒然后重新进入entryset等待。

谈谈 synchronized 和 ReentrantLock 的区别

syn是通过JVM实现的,而reentrantlock底层是使用cas和volatile,也就是通过AQS实现的,reentrantlock的功能比syn更强大,reentrantlock可以实现可打断的锁,而syn只能一直等。reentrantlock还可以实现公平锁,所谓公平锁就是当从waitset唤醒线程时会根据顺序唤醒。reentrantlock支持可重入,同时支持多个condition,可以通过signal唤醒指定condition的线程。

执行 execute()方法和 submit()方法的区别是什么呢?

execute用于实现runnable接口的线程,而submit用于实现callable的接口,实现runnable接口的是没有返回值的,而callable会返回一个future对象,而future类是基于保护性暂停模式的,该模式将两个线程共同关联到同一guardedobject,实现同步。

CountDownLatch 和 CyclicBarrier 的不同之处?

两者都是通过AQS实现且都是要实现线程同步,countdownlatch通过调用await使线程等待其他线程,其他先调用countdown来不断将state-1,当state等于0时就可以继续运行,但countdownlatch并不能实现复用。而CyclicBarrier可以实现复用,线程通过不断调用await然后进入blocked,直到调用await的线程数达到parties,这些线程就继续运行。

猜你喜欢

转载自blog.csdn.net/TheCarol/article/details/113051941