并发专题(java)

Synchronized 相关问题

Synchronized 用过吗,其原理是什么?
被 Synchronized 修饰过的程序块,在编译前后被编译器生成了
monitorenter 和 ==monitorexit ==两个字节码指令。
这两个指令是什么意思呢?
在虚拟机执行到 monitorenter 指令时,首先要尝试获取对象的锁:
如果这个对象没有锁定,或者当前线程已经拥有了这个对象的锁,把锁
的计数器 +1;当执行 monitorexit 指令时将锁计数器 -1;当计数器
为 0 时,锁就被释放了。
如果获取对象失败了,那当前线程就要阻塞等待,直到对象锁被另外一
个线程释放为止。

什么是可重入性,为什么说 Synchronized 是可重入锁?
可重入性是锁的一个基本要求,是为了解决自己锁死自己的情况。
对 Synchronized 来说,可重入性是显而易见的,如果当前线程已经拥
有了这个对象的锁(而不是已拥有了锁则不能继续获取),就把锁的计
数器 +1,其实本质上就通过这种方式实现了可重入性

JVM 对 Java 的原生锁做了哪些优化?
在 Java 6 之前,Monitor 的实现完全依赖底层操作系统的互斥锁来
实现,如果要将一个线程进行阻塞或唤起都需要操作系统的协助,这就需要从用户态切换到内核态来执行,这种切换代价十分昂贵,很耗处理器时间,现代 JDK 中做了大量的优化。
一种优化是使用自旋锁,即在把线程进行阻塞操作之前先让线程自旋等
待一段时间,可能在等待期间其他线程已经解锁,这时就无需再让线程
执行阻塞操作,避免了用户态到内核态的切换。

现代 JDK 中还提供了三种不同的 Monitor 实现,也就是三种不同的
锁:

  • 偏向锁(Biased Locking
  • 轻量级锁
  • 重量级锁
    这三种锁使得 JDK 得以优化 Synchronized 的运行,当 JVM 检测到不同的竞争状况时,会自动切换到适合的锁实现,这就是锁的升级、降级

1.当没有竞争出现时,默认会使用偏向锁
2.如果有另一线程试图锁定某个被偏向过的对象,JVM 就撤销偏向锁,切换到轻量级锁实现。
3.轻量级锁依赖 CAS 操作 Mark Word 来试图获取锁,如果重试成功,就使用普通的轻量级锁;否则,进一步升级为重量级锁

为什么说 Synchronized 是非公平锁?
非公平主要表现在获取锁的行为上,并非是按照申请锁的时间前后给等待线程分配锁的,每当锁被释放后,任何一个线程都有机会竞争到锁,这样做的目的是为了提高执行性能,缺点是可能会产生线程饥饿现象。

可重入锁 ReentrantLock 及其他显式锁相关问题:

跟 Synchronized 相比,可重入锁 ReentrantLock 其实现原理有什么不同?
其实,锁的实现原理基本是为了达到一个目的: 让所有的线程都能看到某种标记。
Synchronized 通过在对象头中设置标记实现了这一目的,是一种 JVM 原生的锁实现方式,而 ReentrantLock 以及所有的基于 Lock 接口的实现类,都是通过用一个 volitile 修饰的 int 型变量,并保证每个线程都能拥有对该 int 的可见性和原子修改,其本质是基于所谓的 AQS 框架

请尽可能详尽地对比下 Synchronized 和 ReentrantLock 的异同:
ReentrantLock 是 Lock 的实现类,是一个互斥的同步锁
从功能角度,ReentrantLock 比 Synchronized 的同步操作更精细(因为可以像普通对象一样使用),甚至实现 Synchronized 没有的高级功能,如:

  • 等待可中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,对处理执行时间非常长的同步块很有用。
  • 带超时的获取锁尝试:在指定的时间范围内获取锁,如果时间到了仍然无法获取则返回。
  • 可以判断是否有线程在排队等待获取锁。
  • 可以响应中断请求:与 Synchronized 不同,当获取到锁的线程被中断时,能够响应中断,中断异常将会被抛出,同时锁会被释放

ReentrantLock 是如何实现可重入性的?
ReentrantLock 内部自定义了同步器 Sync(Sync 既实现了 AQS,又实现了 AOS,而 AOS 提供了一种互斥锁持有的方式),其实就是加锁的时候通过 CAS 算法,将线程对象放到一个双向链表中,每次获取锁的时候,看下当前维护的那个线程 ID 和当前请求的线程 ID 是否一样,一样就可重入了。

如何让 Java 的线程彼此同步?你了解过哪些同步器?请分别介绍下。
JUC 中的同步器三个主要的成员:CountDownLatch、CyclicBarrier 和 Semaphore,通过它们可以方便地实现很多线程之间协作的功能。

CountDownLatch是一个计数器闭锁,通过它可以完成类似于阻塞当前线程的功能,即:一个线程或多个线程一直等待,直到其他线程执行的操作完成。CountDownLatch用一个给定的计数器来初始化,该计数器的操作是原子操作,即同时只能有一个线程去操作该计数器。调用该类await方法的线程会一直处于阻塞状态,直到其他线程调用countDown方法使当前计数器的值变为零,每次调用countDown计数器的值减1。当计数器值减至零时,所有因调用await()方法而处于等待状态的线程就会继续往下执行。这种现象只会出现一次,因为计数器不能被重置,如果业务上需要一个可以重置计数次数的版本,可以考虑使用CycliBarrier。

CyclicBarrier也是一个同步辅助类,它允许一组线程相互等待,直到到达某个公共屏障点(common barrier point)。通过它可以完成多个线程之间相互等待,只有当每个线程都准备就绪后,才能各自继续往下执行后面的操作。类似于CountDownLatch,它也是通过计数器来实现的。当某个线程调用await方法时,该线程进入等待状态,且计数器加1,当计数器的值达到设置的初始值时,所有因调用await进入等待状态的线程被唤醒,继续执行后续操作。因为CycliBarrier在释放等待线程后可以重用,所以称为循环barrier。CycliBarrier支持一个可选的Runnable,在计数器的值到达设定值后(但在释放所有线程之前),该Runnable运行一次,注,Runnable在每个屏障点只运行一个。

Semaphore与CountDownLatch相似,不同的地方在于Semaphore的值被获取到后是可以释放的,并不像CountDownLatch那样一直减到底。

CyclicBarrier 和 CountDownLatch 看起来很相似,请对比下呢?

  • CountDownLatch 是不可以重置的,所以无法重用,CyclicBarrier 没有这种限制,可以重用。
  • CountDownLatch主要是实现了1个或N个线程需要等待其他线程完成某项操作之后才能继续往下执行操作,描述的是1个线程或N个线程等待其他线程的关系。CyclicBarrier主要是实现了多个线程之间相互等待,直到所有的线程都满足了条件之后各自才能继续执行后续的操作,描述的多个线程内部相互等待的关系。

Java 线程池相关问题

如何在 Java 线程池中提交线程?
线程池最常用的提交任务的方法有两种:
1 . execute():ExecutorService.execute 方法接收一个 Runable 实例,它用来执行一个任务:
2.submit():ExecutorService.submit() 方法返回的是 Future 对象

Java 中的线程池是如何实现的?

  • 在 Java 中,所谓的线程池中的“线程”,其实是被抽象为了一个静态内部类 Worker,它基于 AQS 实现,存放在线程池的HashSet workers 成员变量中;
  • 而需要执行的任务则存放在成员变量 workQueue(BlockingQueue workQueue)中。 这样,整个线程池实现的基本思想就是:从 workQueue 中不断取出需要执行的任务,放在 Workers 中进行处理。

线程池中的线程是怎么创建的?是一开始就随着线程池的启动创建好的吗?
显然不是的。线程池默认初始化后不启动 Worker,等待有请求时才启动。
每当我们调用 execute() 方法添加一个任务时,线程池会做如下判断:

  • 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
  • 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
  • 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
  • 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常 RejectExecutionException。

创建线程池的几个核心构造参数?

  • corePoolSize:线程池的核心线程数

  • maximumPoolSize:线程池允许的最大线程数。

  • keepAliveTime:超过核心线程数时闲置线程的存活时间

  • workQueue:任务执行前保存任务的队列,保存由 execute 方法提交
    的 Runnable 任务

     							***帅气的远远啊***
    
发布了87 篇原创文章 · 获赞 79 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41585840/article/details/104944908