本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
按公平程度区分 公平锁和非公平锁
公平锁
是指多个线程按照申请锁的顺序来获取锁,类似排队打饭,先来后到。
非公平锁
是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁在高并发的情况下,有可能会造成优先级反转或者饥饿现象 两者之前区别
公平锁/非公平锁 并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,默认是非公平锁
关于两者区别:
公平锁:
Threads acquire a fair lock in the order in which they requested it
公平锁,就是很公平,在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一 个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己
非公平锁: a nonfair lock permits barging: threads requesting a lock can jump ahead of the queue of waiting threads if the lock happens to be available when it is requested.
非公平锁比较粗鲁,上来就 直接尝试占有锁,面果尝试失败,就再采用类似公平锁那种方式。
synchronized也是非公平锁
Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。
对于Synchronized而言,也是一种非公平锁
可重入锁(递归锁)
可重入锁(也叫做递归锁)
指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁 也即是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块
case one
synchronized就是一个典型的可重入锁
t1 invoked s sencdSMS() t1线程在外层方法获取锁的时候
t1 ####invoked sendEmail() t1 在进入内层方法会自动获取锁
t2 invoked s sendSMS()
t2 ######invoked sendEmail()
case two Reentrantlock就是一个典型的可重入锁
lock锁必须成对出现,有闭必有开,几个无所谓
package com.wsx.reentrantLock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class ThreadDemo implements Runnable{
public synchronized void method1(){
System.out.println(Thread.currentThread().getName()+"!!!!!!!!method1");
method2();
}
public synchronized void method2(){
System.out.println(Thread.currentThread().getName()+"!!!!!!!!method2");
}
//实现runnaable接口,然后new里面先找run()方法
@Override
public void run() {
get();
}
Lock lock = new ReentrantLock();
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()+"!!!!!!!set()");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ReenterLockDemo {
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(()->{
threadDemo.method1();
},"t1").start();
new Thread(()->{
threadDemo.method1();
},"t2").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//实现runnaable接口,然后new里面先找run()方法
Thread t3 = new Thread(threadDemo);
Thread t4 = new Thread(threadDemo);
t3.start();
t4.start();
}
}
自旋锁
循环调用,循环访问,循环探查
自旋锁(spinlock)
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU
好处:不用阻塞
缺点:长时间不能获得,循环会变重,性能会下降
package com.wsx.spinLock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
class resources{
//原子引用线程
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void mylock(){
//获取当前线程
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"!!!!!come in");
while (!atomicReference.compareAndSet(null,thread)){
System.out.println(Thread.currentThread().getName()+"waiting");
}
}
public void unmylock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"!!!!!come out");
atomicReference.compareAndSet(thread,null);
}
}
public class SpinLockDemo {
public static void main(String[] args) {
resources resources = new resources();
new Thread(()->{
resources.mylock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
resources.unmylock();
},"AA").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
resources.mylock();
resources.unmylock();
},"BB").start();
}
}