1、synchronized用过吗,其原理是什么
这是一道Java面试中几乎百分之百都会问到的题目,如果没有参与过并发编程的话,对其是说不上来的。synchronized是由JVM实现的一种实现互斥同步的方式。
Synchronized是由JVM实现的一种实现互斥同步的一种方式,如果你查看被Synchronized修饰过的程序块编译后的字节码,会发现,被Synchronized修饰过的程序块,在编译前后被编译器生成了monitorenter和monitorexit两个字节码指令。
在虛拟机执行到monitorenter指令时,首先要尝试获取对象的锁:
- 如果这个对象没有锁定,或者当前线程已经拥有了这个对象的锁,把锁的计数器+1;
- 当执行 monitorexit指令时将锁计数器-1;
- 当计数器为O时,锁就被释放了。如果获取对象失败了,那当前线程就要阻塞等待,直到对象锁被另外一个线程释放为止。
- Java中Synchronize通过在对象头设置标记,达到了获取锁和释放锁的目的。
2、你刚才提到获取对象的锁,这个锁是什么?如何确定对象的锁?
“锁”的本质其实是monitorenter和monitorexit字节码指令的一个Reference类型的参数,即要锁定和解锁的对象。我们知道,使用Synchronized可以修饰不同的对象,因此,对应的对象锁可以这么确定。
- 如果Synchronized明确指定了锁对象,比如Synchronized(变量名)、Synchronized(this)等,说明加解锁对象为该对象。
- 如果没有明确指定:
- 若Synchronized修饰的方法为非静态方法,表示此方法对应的对象为锁对象;
- 若Synchronized修饰的方法为静态方法,则表示此方法对应的类对象为锁对象;
3、什么是可重入性?为什么说synchronized是可重入锁?
所谓可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。
synchronized是基于原子性的内部锁机制,是可重入的,因此在一个线程调用synchronized方法的同时在其方法体内部调用该对象另一个synchronized方法,也就是说一个线程得到一个对象锁后再次请求该对象锁,是允许的,这就是synchronized的可重入性。
4、Java中的线程池是如何实现的?
线程是稀缺资源,如果被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,合理的使用线程池对线程进行统一分配、调优和监控,有以下好处:
- 1、降低资源消耗;
- 2、提高响应速度;
- 3、提高线程的可管理性。
Executors是java线程池的工厂类,通过它可以快速初始化一个符合业务需求的线程池,如Executors.newFixedThreadPool
方法可以生成一个拥有固定线程数的线程池。
其本质是通过不同的参数初始化一个ThreadPoolExecutor对象,具体参数描述如下:
corePoolSize
线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;如果执行了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程。
maximumPoolSize
线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize;
keepAliveTime
线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间;默认情况下,该参数只在线程数大于corePoolSize时才有用;
workQueue:
用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在JDK中提供了如下阻塞队列:
- 1、ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
- 2、LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
- 3、SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
- 4、priorityBlockingQuene:具有优先级的无界阻塞队列;
5、如何在java线程池中提交线程?
线程池框架提供了两种方式提交任务,根据不同的业务需求选择不同的方式。
Executor.execute()
通过Executor.execute()方法提交的任务,必须实现Runnable接口,该方式提交的任务不能获取返回值,因此无法判断任务是否执行成功。
ExecutorService.submit()
通过ExecutorService.submit()方法提交的任务,可以获取任务执行完的返回值。