1、实现线程同步有三种方式?
1. 同步代码块:在代码块上加上“synchronized”关键字的话,则此代码块就称为同步代 码块。
//同步代码块格式:
synchronized(监视对象){
//需要同步的代码 ;
}
解释:监视对象有三种:对象、String、.class 文件(只要是不变的对象都可以做监 视对象)
1. 同步方法
//同步方法定义格式:
synchronized 方法返回值 方法名称(参数列表){
}
//在方法上加 synchronized,是把当前对象做为监视器
1. 同步锁
Lock lock = new ReentrantLock();//(可以在类中直接 new)
lock.lock(); //中间的代码块进行加锁 lock.unlock();
2、Java中的锁有几种方式?
1. Synchronized
2. Lock
Synchronized的局限性:
● a. 如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能等待。(不能主动释放锁)
● b.当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。(不分情况,一律锁死)
3、Lock的几个实现类?
● ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”。
● ReadWriteLock,顾名思义,是读写锁。它维护了一对相关的锁 ——“读取锁”和“写入锁”,一个用于读取操作,另一个用于写入操作。他的两个实现类读锁readerLock和写锁writerLock。
4、synchronized 和 Lock 的区别和应用场景?
1. Lock 是接口,而 synchronized 是 Java 中的关键字,synchronized 是内置的语言实现;
2. synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而 Lock 在发生异常时,如果没有主动通过 unLock()去释放锁,则很可能造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁;
3. Lock 可以让等待锁的线程响应中断,而 synchronized 却不行,使用synchronized 时,等待的线程会一直等待下去,不能够响应中断;
4. 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
5. Lock 可以提高多个线程进行读操作的效率。
6. Lock 能完成 Synchronized 所实现的所有功能在性能上来说,如果竞争资源不激烈,Synchronized 要优于 Lock,而当竞争资源非常激烈时(即有大量线程同时竞争),此时 Lock 的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
5、synchronized 和 ReentrantLock 区别是什么?
1)底层实现
synchronized 是JVM层面的锁,是Java关键字。ReentrantLock 是从jdk1.5以来(java.util.concurrent.locks.Lock)提供的API层面的锁。
synchronized通过monitor对象来完成(monitor enter与monitor exit),对象只有在同步块或同步方法中才能调用wait/notify方法。 synchronized的实现涉及到锁的升级,具体为无锁、偏向锁、自旋锁、向OS申请重量级锁。
ReentrantLock实现则是通过利用CAS(CompareAndSwap)自旋机制保证线程操作的原子性和volatile保证数据可见性以实现锁的功能。
2)是否可手动释放:
synchronized 不需要用户去手动释放锁,synchronized 代码执行完后系统会自动让线程释放对锁的占用;
ReentrantLock则需要用户去手动释放锁,如果没有手动释放锁,就可能导致死锁现象。一般通过lock()和unlock()方法配合try/finally语句块来完成,使用释放更加灵活。
3)是否可中断
synchronized是不可中断类型的锁,除非加锁的代码中出现异常或正常执行完成; ReentrantLock则可以中断,可通过trylock(long timeout,TimeUnit unit)设置超时方法或者将lockInterruptibly()放到代码块中,调用interrupt方法进行中断。
4)锁的对象
synchronzied锁的是对象,锁是保存在对象头里面的,根据对象头数据来标识是否有线程获得锁/争抢锁;ReentrantLock锁的是线程,根据进入的线程和int类型的state标识锁的获得/争抢。
6、synchronized与volatile、Lock、ReentrantLock的区别
synchronized与volatile
项 | synchronized | volatile |
作用 | 锁定当前对象,只有当前线程可以访问该对象,其他线程被阻塞。 | 本质是在告诉 jvm 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; |
使用范围 | 变量、方法、和类 | 变量 |
线程安全 | 可以保证变量的可见性和原子性 | 仅能实现变量的可见性,不能保证原子性 |
阻塞 | 可能会造成线程的阻塞 | 不会造成线程的阻塞 |
优化 | 标记的变量可以被编译器优化 | 标记的变量不会被编译器优化 |
synchronized与Lock
项 | synchronized | Lock |
存在层面 | java 内置关键字,在 jvm 层面 | java 类(实际是一个接口),在API层面 |
锁的释放 | 会自动释放锁: 线程执行完同步代码会释放锁 ; 线程执行中发生异常会释放锁 | 需在 finally 中手工释放锁(unlock()方法释放锁) |
锁的获取 | 若线程 1 获得锁,线程 2 等待; 若线程 1 阻塞,线程 2 会一直等待 | 分情况而定,Lock有多个获得锁的方式。 可尝试获得锁(tryLock()),线程可以不用一直等待 |
锁状态 | 无法判断是否已经获取锁 | 可判断是否已经获取到锁 |
锁类型 | 可重入、不可中断、非公平 (只能等待锁释放,不能响应中断) | 可重入、可中断、可公平(两者皆可) (等待锁时可用interrupt来中断等待) |
适用场景 | 简单的线程同步控制。 多线程竞争的概率很高。 | 复杂的线程同步控制(比如:公平锁、读写锁)。多线程竞争的概率很低。 |
性能 | jdk1.6以前:重量级锁(无法取得锁即挂起,性能差) jdk1.6之后:优化了性能:给它的锁加入了四种状态,无锁状态 -> 偏向锁 -> 轻量级锁 -> 重量级锁,自动进行锁的升级。 | jdk1.6以后: Lock性能略好于synchronized |
synchronized与ReentrantLock
相同点
项 | 说明 |
同步方式 | 都是加锁方式同步 |
是否可重入 | 都是可重入锁 |
是否阻塞 | 都是阻塞式的同步 |
不同点
因为ReentrantLock实现了Lock,所以拥有synchronized与Lock的所有不同点,其他不同点如下:
项 | synchronized | ReentrantLock |
锁条件个数 | 1个 | 可以多个 |