公平锁、非公平锁
什么是公平锁与非公平锁?
公平锁:先来先服务。
非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先级获取锁在高并发的情况下,有可能会造成优先级反转或者饥饿现象。
默认是非公平锁,非公平锁的优点在于比公平锁大。
如何设置公平锁?
//参数默认是false,表示非公平锁,参数为true时,表示为公平锁。
ReentrantLock re = new ReentrantLock(true);
ReentrantLock re1 = new ReentrantLock();
源代码如下:
公平锁:
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
非公平锁:
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
可重入锁(递归锁)
什么是可重入锁?
指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取线程的代码,在同一个线程在外层方法获取到锁的时候,在进入内层方法会自动获取该锁,也就是说,线程可以进入到任何一个它已经拥有的锁同步着的代码块。
示例如下:
只要拿到method01的锁,method02的锁也就是拿到了(因为进入了method01方法内)。并且锁是一样的,就是method01的锁。
典型的可重入锁:ReentrantLock、Synchronized
作用:避免死锁。
代码示例
synchronized代码演示:
class Phone{//资源类
public synchronized void sendMS()throws Exception{
System.out.println("进入发送信息函数");
sendEmail();
}
public synchronized void sendEmail(){
System.out.println("进入发送邮件函数");
}
}
public class Test {
public static void main(String[] args){
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendMS();
} catch (Exception e) {
e.printStackTrace();
}
},"t1").start();
new Thread(()->{
try {
phone.sendMS();
} catch (Exception e) {
e.printStackTrace();
}
},"t2").start();
}
}
运行结果:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Phone implements Runnable{//资源类
public synchronized void sendMS()throws Exception{
System.out.println(Thread.currentThread().getName()+"进入发送信息函数");
sendEmail();
}
public synchronized void sendEmail(){
System.out.println(Thread.currentThread().getName()+"进入发送邮件函数");
}
//+++++++++++++++++++++++++以下是ReentrantLock+++++++++++++++++++++++++++++++++++++
Lock lock = new ReentrantLock();
@Override
public void run() {
get();
}
public void get(){
lock.lock();
try {
//逻辑代码
System.out.println(Thread.currentThread().getName()+"获取get()");
set();
}catch(Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void set(){
lock.lock();
try {
//逻辑代码
System.out.println(Thread.currentThread().getName()+"设置get()");
}catch(Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class Test {
public static void main(String[] args){
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendMS();
} catch (Exception e) {
e.printStackTrace();
}
},"t1").start();
new Thread(()->{
try {
phone.sendMS();
} catch (Exception e) {
e.printStackTrace();
}
},"t2").start();
Thread t3 = new Thread(phone);
Thread t4 = new Thread(phone);
t3.start();
t4.start();
}
}
小提示:
只要是加锁和解锁都是成套出现时,程序的编译和运行就是对的,反之则程序出现问题。
自旋锁
什么是自旋锁?
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
题目:手写一个自旋锁
自旋锁的本质就是while+CAS方法
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
public class Mylock {
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock(){
//获取当前线程
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"线程获取到锁");
while (!atomicReference.compareAndSet(null,thread)){
}
}
public void myUnLock(){
//获取当前线程
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread,null);
System.out.println(Thread.currentThread().getName()+"线程释放锁");
}
public static void main(String[] args) {
Mylock mylock = new Mylock();
new Thread(()->{
mylock.myLock();
try{
TimeUnit.SECONDS.sleep(5);
}catch (Exception e){
e.printStackTrace();
}
mylock.myUnLock();
},"t1").start();
//让主线程休息一秒,确保t1线程已启动。
try{
TimeUnit.SECONDS.sleep(1);
}catch (Exception e){
e.printStackTrace();
}
//t2线程开始启动
new Thread(()->{
mylock.myLock();
try{
TimeUnit.SECONDS.sleep(5);
}catch (Exception e){
e.printStackTrace();
}
mylock.myUnLock();
},"t2").start();
}
}
运行结果:
读写锁
在写的过程中不允许被打断,必须一次性写完,但是读没有限制。
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class Abc{
private volatile Map<String,Object> map = new HashMap<>() ;
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
public void put(String key,Object value){
reentrantReadWriteLock.writeLock().lock();
try{
map.put(key,value);
}catch (Exception e){
e.printStackTrace();
}finally {
reentrantReadWriteLock.writeLock().unlock();
}
}
public void get(String key){
reentrantReadWriteLock.readLock().lock();
try{
Object integer = map.get(key);
}catch (Exception e){
e.printStackTrace();
}finally {
reentrantReadWriteLock.readLock().unlock();
}
}
}
public class ReadAndWriteLock {
public static void main(String[] args) {
Abc abc =new Abc();
for(int i = 0;i<5;i++){
int temp = i;
new Thread(()->{
abc.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
for(int i = 0;i<5;i++){
int temp = i;
new Thread(()->{
abc.get(temp+"");
},String.valueOf(i)).start();
}
}
}