概述
公平和非公平锁
- 公平锁:是指多个线程按照申请锁的顺序获取锁。先来后到。
- 非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发下,有可能会造成优先级反转或者饥饿现象。
非公平锁的优点在于吞吐量比公平锁大。
ReentrantLock默认是非公平锁
Lock lock = new ReentrantLock();
public ReentrantLock() {
sync = new NonfairSync();
}
其有两个构造器。无参构造会创建一个非公平锁。
有参构造的参数是一个boolean值,true是公平锁,false是非公平锁(和无参构造一样)。
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
对于synchronized而言,也是一种非公平锁。
可重入锁(递归锁)
可重入锁(也叫递归锁)。
指的是线程获取锁之后,可以再次获取锁而不会发生死锁。
也就是获取锁之后进入method1方法,调用的method2方法(此时method2也有锁),只不过method1和method2是一把锁。
举个现实生活中的例子:到家时用钥匙打开门后,就可以直接进入厨房或者卫生间。而不需要在用其他钥匙。
public sync method1(){
method2();
}
public sync method2(){}
可重入锁最大的作用就是解决死锁问题。
ReentrantLock和synchronized都是可重入锁。
代码演示
此代码借用了博主https://blog.csdn.net/w8y56f/article/details/89554060的代码。
synchronized
package locks;
/*
* 可重入锁(也叫递归锁)
* 是指同一线程中,外层函数获取锁之后,在内层递归函数仍然能获取该锁的代码。
* 在同一线程在外层获取锁的时候,在进入内层方法会自动获取锁。
*
* 也就是说,线程可以进入任何一个它拥有锁所同步的代码块。
*
* */
public class ReentrantLockDemo {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (this){
System.out.println("第1次获取锁,这个锁是:"+this);
int index = 1;
while (true){
synchronized (this){
System.out.println("第"+(++index)+"次获取锁,这个锁是:"+this);
}
if(index == 10){
break;
}
}
}
}
}).start();
}
}
可以看到,同一个线程可以多次获取锁,而且没有发生死锁。
ReentrantLock
package locks;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo2 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(new Runnable() {
@Override
public void run() {
try{
lock.lock();
System.out.println("第1次获取锁,这个锁是:"+lock);
int index = 1;
while (true){
try{
lock.lock();
System.out.println("第"+(++index)+"次获取锁,这个锁是:"+lock);
try{
Thread.sleep(new Random().nextInt(200));
}catch (Exception e){
e.printStackTrace();
}
if(index == 10){
break;
}
}finally {
lock.unlock();
}
}
}finally {
lock.unlock();
}
}
}).start();
}
}
注意:使用ReentrantLock,加锁和解锁必须是成对出现的。
例
正常情况
package locks;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class T1 implements Runnable{
Lock lock = new ReentrantLock();
public void get(){
lock.lock();
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"t invoked get()");
set();
}finally {
lock.unlock();
lock.unlock();
}
}
public void set(){
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"t invoked set()");
}finally {
lock.unlock();
}
}
@Override
public void run() {
get();
}
public static void main(String[] args) {
T1 t1 = new T1();
Thread t3 = new Thread(t1);
Thread t4 = new Thread(t1);
t3.start();
t4.start();
}
}
加锁比解锁多,会导致t4线程卡死,也就是说多出的lock,还没有释放锁,自然而然t4会一直等待。
package locks;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class T1 implements Runnable{
Lock lock = new ReentrantLock();
public void get(){
lock.lock();
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"t invoked get()");
set();
}finally {
//lock.unlock();
lock.unlock();
}
}
public void set(){
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"t invoked set()");
}finally {
lock.unlock();
}
}
@Override
public void run() {
get();
}
public static void main(String[] args) {
T1 t1 = new T1();
Thread t3 = new Thread(t1);
Thread t4 = new Thread(t1);
t3.start();
t4.start();
}
}
解锁比加锁多:会直接运行出错。
package locks;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class T1 implements Runnable{
Lock lock = new ReentrantLock();
public void get(){
lock.lock();
// lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"t invoked get()");
set();
}finally {
lock.unlock();
lock.unlock();
}
}
public void set(){
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"t invoked set()");
}finally {
lock.unlock();
}
}
@Override
public void run() {
get();
}
public static void main(String[] args) {
T1 t1 = new T1();
Thread t3 = new Thread(t1);
Thread t4 = new Thread(t1);
t3.start();
t4.start();
}
}
自旋锁
自旋锁指的是尝试获取锁的线程不会立即阻塞,而是会通过循环不断的去尝试获取锁。这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
代码验证
package locks;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
public class SpinDemo {
//原子引用线程 初始值null
AtomicReference<Thread> reference = new AtomicReference<>();
public void mylock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"t come in ^_^");
//如果当前没有线程占用锁,则自己占用。 atomicInteger中cas思想
while (!reference.compareAndSet(null,thread)){
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"t 自旋");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void myunlock(){
Thread thread = Thread.currentThread();
//解锁操作,如果自己用完锁,则替换为null
reference.compareAndSet(thread,null);
System.out.println(Thread.currentThread().getName()+"t invoked myunlock");
}
public static void main(String[] args) {
SpinDemo spinDemo = new SpinDemo();
new Thread(()->{
spinDemo.mylock();
try{
TimeUnit.SECONDS.sleep(5);
}catch(InterruptedException e){
e.printStackTrace();
}
spinDemo.myunlock();
},"t1").start();
try{
TimeUnit.SECONDS.sleep(1);
}catch(InterruptedException e){
e.printStackTrace();
}
new Thread(()->{
spinDemo.mylock();
spinDemo.myunlock();
},"t2").start();
}
}
t1在占用锁的时间里,t2一直会尝试获取锁。
最后
以上就是优雅皮带为你收集整理的公平锁/非公平锁/可重入锁/递归锁/自旋锁的全部内容,希望文章能够帮你解决公平锁/非公平锁/可重入锁/递归锁/自旋锁所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复