Java 面试—CycliBarriar 对比 CountdownLatch,乐/悲观锁,wait/notify/notifyAll对比

 

目录

什么是Callable和Future?

CycliBarriar和CountdownLatch有什么区别?

线程进入某个对象的synchronized的实例方法后,其它线程是否可进入此对象的其它方法?

乐观锁和悲观锁的理解及如何实现?

CopyOnWriteArrayList可以用于什么应用场景?

什么叫线程安全?servlet是线程安全吗?

java中wait和sleep方法的不同?

Java中notify 和 notifyAll有什么区别?

怎么检测一个线程是否拥有锁?


什么是Callable和Future?

1、Callable 接口类似于 Runnable,但是 Runnable 不会返回结果,并且无法抛出返回结果的异常,而 Callable 功能更强大一些,被线程执行后,可以返回值,这个返回值可以被 Future 拿到,也就是说 Future 可以拿到异步执行任务的返回值。

2、Future 接口表示异步任务,是还没有完成的任务给出的未来结果。所以说 Callable 用于产生结果,Future 用于获取结果。

3、Runable 出自 JDK1.0,Callable 出自 JDK 1.5,可以参考《submit 提交Callable任务

CycliBarriar和CountdownLatch有什么区别?

1、CyclicBarrier 可以重复使用(reset方法可置0),而 CountdownLatch 不能重复使用。

2、Java 的 concurrent 包里面的 CountDownLatch 其实可以把它看作一个计数器,只不过这个计数器的操作是原子操作,同时只能有一个线程去操作这个计数器,也就是同时只能有一个线程去减这个计数器里面的值。

3、可以向 CountDownLatch 对象设置一个初始的数字作为计数值,任何调用这个对象上的await()方法都会阻塞,直到这个计数器的计数值被其他的线程减为0为止。

4、所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。

5、CyclicBarrier一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。

6、可以参考《 CountDownLatch 倒计数锁存器 与 CyclicBarrier 循环栅栏

线程进入某个对象的synchronized的实例方法后,其它线程是否可进入此对象的其它方法?

1、如果其他方法没有 synchronized 修饰的话,其他线程是可以进入的。

2、所以要开放一个线程安全的对象时,得保证每个方法都是线程安全的。

3、可以参考《 synchronized 添加对象锁与类锁

乐观锁和悲观锁的理解及如何实现?

悲观锁

1、总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。

2、传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。

乐观锁

1、每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。

2、乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

CopyOnWriteArrayList可以用于什么应用场景?

1、CopyOnWriteArrayList 的好处之一是当多个迭代器同时遍历和修改这个列表时,不会抛出ConcurrentModificationException。在CopyOnWriteArrayList中,写入将导致创建整个底层数组的副本,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安全地执行。

2、由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致 young gc 或者 full gc;

3、不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求;

4、CopyOnWriteArrayList透露的思想

读写分离,读和写分开

最终一致性

使用另外开辟空间的思路,来解决并发冲突

什么叫线程安全?servlet是线程安全吗?

1、线程安全是编程中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。

2、Servlet不是线程安全的,servlet是单实例多线程的,当多个线程同时访问同一个方法,是不能保证共享变量的线程安全性的。

3、Struts2的action是多实例多线程的,是线程安全的,每个请求过来都会new一个新的action分配给这个请求,请求完成后销毁。

4、SpringMVC的Controller和Servlet采用类似的处理流程,所以同样线程不安全。

5、Struts2好处是不用考虑线程安全问题;Servlet和SpringMVC需要考虑线程安全问题,但是性能可以提升不用处理太多的gc,可以使用ThreadLocal来处理多线程的问题。

java中wait和sleep方法的不同?

1、最大的不同是在等待是 wait 会释放锁,而 sleep 一直持有锁。

2、Wait 通常被用于线程间交互,sleep通常被用于暂停执行。

3、wait()方法会释放 CPU执行权和占有的锁。

4、sleep(long)方法仅释放CPU使用权,锁仍然占用;线程被放入超时等待队列,与Thread.yield() 相比,它会使线程较长时间得不到运行。

5、Thread.yield() 方法仅释放CPU执行权,锁仍然占用,线程会被放入就绪队列,会在短时间内再次执行。

6、wait 和 notify、notifyAll 必须配套使用,即必须使用同一把锁调用;

7、wait和notify必须放在一个同步块中调用,wait和notify的对象必须是他们所处同步块的锁对象。

8、可以参考《 Wait 线程阻塞 与 Notify、NotifyAll 线程唤醒

Java中notify 和 notifyAll有什么区别?

1、notify() 方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。

2、notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行。

3、可以参考《 Wait 线程阻塞 与 Notify、NotifyAll 线程唤醒

怎么检测一个线程是否拥有锁?

1、java.lang.Thread 中有一个方法叫 holdsLock(Object obj),它返回true则表示当前线程拥有某个具体对象的锁。

public static native boolean holdsLock(Object obj);

猜你喜欢

转载自blog.csdn.net/wangmx1993328/article/details/88375629