java线程 - ReetrantLock

小编 2026-06-05 阅读:1200 评论:0
一. 公平锁&非公平锁lock&unlock() demo   ReetrantLock公平锁demo: package com.mzs.demo.entity; import java...

一. 公平锁&非公平锁lock&unlock() demo 


 ReetrantLock公平锁demo:

package com.mzs.demo.entity;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestDemo {
	
	static Lock lock = new ReentrantLock(true);

	public static void main(String[] args) {
		for (int i = 0; i < 5; i++) {
			new Thread(new ThreadDemo(i)).start();
		}
	}
	
	static class ThreadDemo implements Runnable {
		
		int i = 0;
		
		ThreadDemo(int i) {
			this.i = i;
		}
		
		public void run() {
			try {
				for (int j = 0; j < 2; j++) {
					lock.lock();
					TimeUnit.MILLISECONDS.sleep(100);
					System.out.println(\"[\" + i + \"] get the lock\");
					lock.unlock();
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

控制台输出: 

[0] get the lock
[1] get the lock
[2] get the lock
[3] get the lock
[4] get the lock
[0] get the lock
[1] get the lock
[2] get the lock
[3] get the lock
[4] get the lock


ReetrantLock非公平锁demo:

package com.mzs.demo.entity;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestDemo {
	
	static Lock lock = new ReentrantLock();

	public static void main(String[] args) {
		for (int i = 0; i < 5; i++) {
			new Thread(new ThreadDemo(i)).start();
		}
	}
	
	static class ThreadDemo implements Runnable {
		
		int i = 0;
		
		ThreadDemo(int i) {
			this.i = i;
		}
		
		public void run() {
			try {
				for (int j = 0; j < 2; j++) {
					lock.lock();
					TimeUnit.MILLISECONDS.sleep(100);
					System.out.println(\"[\" + i + \"] get the lock\");
					lock.unlock();
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

只改变了ReetrantLock构造器的参数。默认就是非公平锁。

控制台输出:

[0] get the lock
[0] get the lock
[1] get the lock
[2] get the lock
[2] get the lock
[3] get the lock
[4] get the lock
[4] get the lock
[1] get the lock
[3] get the lock
 


二. 公平锁&非公平锁-lock()源码解析

    public ReentrantLock() {
        sync = new NonfairSync();
    }  
 
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

 通过参数fair来判断选择的是公平锁还是非公平锁。默认就是非公平锁。


非公平锁的lock()方法 

  static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
    }

非公平锁机制是线程直接尝试获取锁,不考虑其他条件。获取失败,重新加入等待队列的尾部。

获取失败执行的acquire()方法,使用的AQS的源码。这里不再详述。

  public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

state:表示获取锁的状态。即获得锁的次数。

  /**
     * The synchronization state.
     */
    private volatile int state;

上述源码是AbstractQueuedSynchronizer的源码。


接下来看公平锁的lock()方法

它的机制是:基于FIFO。先进入等待队列的,先尝试获取锁。避免了饥饿问题。

  static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don\'t grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error(\"Maximum lock count exceeded\");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

acquire()方法在上面提到过。

实际考虑的方法是:tryAcquire()方法。

需要先考虑state的值。等于0表示此时还未持有锁。还需查看它之前有没有前驱,如果没有,再获取锁。

不等于0的时候表示此时已经多次持有锁。在原有的基础上增加state的值。表示持有几次锁。


 三. 公平锁&非公平锁 - unlock()源码解析

   public void unlock() {
        sync.release(1);
    }

 

  public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

首先需要判断tryRelease(arg)

接下来看看tryRelease()源码:

      protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

当前state的值-释放锁的次数,结果需要等于0以及当前线程是独占模式下的锁,才可以释放锁成功。之后state值变成0,表示不再持有锁。


四. lockInterruptibly()方法 - demo

package com.mzs.demo.entity;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestDemo {
	
	public static void main(String[] args) {
		Lock lock1 = new ReentrantLock();
		Lock lock2 = new ReentrantLock();
		Thread thread1 = new Thread(new ThreadDemo(lock1, lock2));
		Thread thread2 = new Thread(new ThreadDemo(lock2, lock1));
		thread1.start();
		thread2.start();
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
		}
		thread1.interrupt();
	}
	
	static class ThreadDemo implements Runnable {
		
		Lock firstLock = new ReentrantLock();
		Lock secondLock = new ReentrantLock();
		
		ThreadDemo(Lock firstLock, Lock secondLock) {
			this.firstLock = firstLock;
			this.secondLock = secondLock;
		}

		public void run() {
			try {
				firstLock.lockInterruptibly();
				Thread.sleep(1000);
				secondLock.lockInterruptibly();
			} catch (InterruptedException e) {
			} finally {
				firstLock.unlock();
				secondLock.unlock();
				System.out.println(Thread.currentThread().getName() + \"unlock successfully\");
			}
		}
		
	}
}

 上述程序运行之后会造成死锁。但是通过中断thread0。获取锁的thread0响应中断,释放锁。从而thread1得以正常运行。

控制台输出:

Thread-1unlock successfully
Exception in thread \"Thread-0\" java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
    at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
    at com.mzs.demo.entity.TestDemo$ThreadDemo.run(TestDemo.java:40)
    at java.lang.Thread.run(Thread.java:748)
 


五. lockInterruptibly()源码解析

   public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

 

   public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

如果尝试获取锁时,被中断了,则会抛出异常。如果尝试获取锁失败了,执行接下来的方法。

  private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

先来看addWaiter()方法:

 private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

查看此时的等待队列的尾节点。如果不为空将node (当前线程与指定的模式组成的节点),加入到尾节点的后面。然后用CAS的方式,指定node为新的尾节点。然后将之前的尾节点的后继节点设置为node。

再回来看这个方法:

   private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

查看当前等待队列的节点node。判断它的前驱节点是否是头结点并且是否成功尝试获取锁,如果都满足的话,设置新的头结点为node,之前的头结点需要gc回收,只需要设置它的后继节点为空即可。如果上述两个条件有一个不满足的话就会执行接下来的方法:

  private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don\'t park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

待续。。。。。。

demo来源于:https://www.imooc.com/article/45194

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表