多线程编程()--Juc锁框架之ReentrantLock

一、ReentrantLock概述。

1.1 含义概述

ReentrantLock一个可重入的互斥锁,又被称为“独占锁”,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。ReentrantLock将由最近成功获得锁,并且还没有释放该锁的线程所拥有。当锁没有被另一个线程所拥有时,调用Lock的线程将成功获取该锁并返回。如果当前线程已经拥有该锁,此方法将立即返回。

ReentrantLock在同一时间点只能被一个线程持有,可重入的意思即表示ReentrantLock可以被单个线程多次获取。ReentrantLock分为公平锁和非公平锁。它们的区别是在锁的获取机制上是否公平。锁是为了保护竞争资源,防止多个线程同时操作共享资源而出错。

1.2 方法列表

// 创建一个 ReentrantLock ,默认是“非公平锁”。
ReentrantLock()
// 创建策略是fair的 ReentrantLock。fair为true表示是公平锁,fair为false表示是非公平锁。
ReentrantLock(boolean fair)

// 查询当前线程保持此锁的次数。
int getHoldCount()
// 返回目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null。
protected Thread getOwner()
// 返回一个 collection,它包含可能正等待获取此锁的线程。
protected Collection<Thread> getQueuedThreads()
// 返回正等待获取此锁的线程估计数。
int getQueueLength()
// 返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程。
protected Collection<Thread> getWaitingThreads(Condition condition)
// 返回等待与此锁相关的给定条件的线程估计数。
int getWaitQueueLength(Condition condition)
// 查询给定线程是否正在等待获取此锁。
boolean hasQueuedThread(Thread thread)
// 查询是否有些线程正在等待获取此锁。
boolean hasQueuedThreads()
// 查询是否有些线程正在等待与此锁有关的给定条件。
boolean hasWaiters(Condition condition)
// 如果是“公平锁”返回true,否则返回false。
boolean isFair()
// 查询当前线程是否保持此锁。
boolean isHeldByCurrentThread()
// 查询此锁是否由任意线程保持。
boolean isLocked()
// 获取锁。
void lock()
// 如果当前线程未被中断,则获取锁。
void lockInterruptibly()
// 返回用来与此 Lock 实例一起使用的 Condition 实例。
Condition newCondition()
// 仅在调用时锁未被另一个线程保持的情况下,才获取该锁。
boolean tryLock()
// 如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁。
boolean tryLock(long timeout, TimeUnit unit)
// 试图释放此锁。
void unlock()

二、源码分析

package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
import java.util.Collection;ReentrantLock

/**
 *
 */
public class ReentrantLock implements Lock, java.io.Serializable {
	 /* 序列号 */
    private static final long serialVersionUID = 7373984872572414699L;
    /** 同步队列 */
    private final Sync sync;

    /**
     * 实现锁的机制是通过Sync进行操作的。Sync类是继承AbstractQueuedSynchronizer类的。这也就表明ReentrantLock是基于AQS的实现的。
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
		 /* 序列号 */
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * 获取锁
         */
        abstract void lock();

        /**
         *  非公平方式获取
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {// 表示没有线程正在竞争该锁
                if (compareAndSetState(0, acquires)) {// 比较并设置状态成功,状态0表示锁没有被占用
					// 设置当前线程独占
                    setExclusiveOwnerThread(current);
					// 成功
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {// 当前线程拥有该锁
			    // 增加重入次数
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
				// 设置状态
                setState(nextc);
                return true;
            }
            return false;
        }

        /**
         *   试图在共享模式下获取对象状态,此方法应该查询是否允许它在共享模式下获取对象状态,如果允许,则获取它
         */		  
        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;
        }
        /**
         *判断资源是否被当前线程占有
         */	
        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
        /**
         * 新生一个条件
         */	
        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class
        /**
         *  返回资源的占用线程
         */	
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
        /**
         *  返回资源占用线程的状态
         */	
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
        /**
         * 返回资源是否被占用
         */	
        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * 自定义反序列化逻辑
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

    /**
     *  非公平锁的同步器
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         *  获得锁
         */
        final void lock() {
            if (compareAndSetState(0, 1)) //比较并设置状态成功,状态0表示锁没有被占用
			    // 把当前线程设置独占了锁
                setExclusiveOwnerThread(Thread.currentThread());
            else// 锁已经被占用,或者set失败
			// 以独占模式获取对象,忽略中断
		/**
		 *   这里说明一下“1”的含义,它是设置“锁的状态”的参数。对于“独占锁”而言,锁处于可获取状态时,它的状态值是0;锁被线程初次获取到了,它的状态值就变成了1。
		 *   由于ReentrantLock(公平锁/非公平锁)是可重入锁,所以“独占锁”可以被单个线程多此获取,每获取1次就将锁的状态+1。
		 *   也就是说,初次获取锁时,通过acquire(1)将锁的状态值设为1;再次获取锁时,将锁的状态值设为2;依次类推...这就是为什么获取锁时,传入的参数是1的原因了。
		 *   可重入就是指锁可以被单个线程多次获取
		 */			
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

    /**
     * 实现公平锁的同步器
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
		//实现父类的抽象获取锁的方法
        final void lock() {
			//调用acquire让线程去同步队列排队以独占模式获取对象,忽略中断
            acquire(1);
        }

        /**
         * 尝试公平获取锁
         */
        protected final boolean tryAcquire(int acquires) {
			 // 获取当前线程
            final Thread current = Thread.currentThread();
			// 获取当前同步状态
            int c = getState();
			//如果同步状态0则表示锁没被占用
            if (c == 0) {
				//判断同步队列是否有前继结点
                if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {// 不存在已经等待更久的线程并且比较并且设置状态成功
                      // 设置当前线程独占
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {// 状态不为0,即资源已经被线程占据
			    // 下一个状态
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
				// 设置状态如果是当前线程持有锁就直接修改同步状态
                setState(nextc);
                return true;
            }
            return false;
        }
    }

    /**
     * ReentrantLock默认的无参构造方法
     */
    public ReentrantLock() {
		 // 默认非公平策略
        sync = new NonfairSync();
    }

    /**
     * 可以传递参数确定采用公平策略或者是非公平策略,参数为true表示公平策略,否则,采用非公平策略
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    /**
     *获得锁
     *
     */
    public void lock() {
        sync.lock();
    }

    /**
     *
     *
     * 如果当前线程未被中断,则获取锁
     *
     */
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    /**
     *
     *
     * 仅在调用时锁未被另一个线程保持的情况下,才获取该锁。
     *
     */
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

    /**
     *
     *
     *
     *
     * 如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁。
     */
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    /**
     * Attempts to release this lock.
     *
     * 试图释放此锁
     *
     */
    public void unlock() {
        sync.release(1);
    }

    /**
     * 返回用来与此 Lock 实例一起使用的 Condition 实例。
     *
     *
     */
    public Condition newCondition() {
        return sync.newCondition();
    }

    /**
     * 
     *
     *
     * 
     *查询当前线程保持此锁的次数。
     */
    public int getHoldCount() {
        return sync.getHoldCount();
    }

    /**
     * 
     *
     * 
     * 
     *
     */
    public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }

    /**
     * Queries if this lock is held by any thread. This method is
     * designed for use in monitoring of the system state,
     * not for synchronization control.
     *
     * @return {@code true} if any thread holds this lock and
     *         {@code false} otherwise
     */
    public boolean isLocked() {
        return sync.isLocked();
    }

    /**
     * Returns {@code true} if this lock has fairness set true.
     *
     * @return {@code true} if this lock has fairness set true
     */
    public final boolean isFair() {
        return sync instanceof FairSync;
    }

    /**
     * Returns the thread that currently owns this lock, or
     * {@code null} if not owned. When this method is called by a
     * thread that is not the owner, the return value reflects a
     * best-effort approximation of current lock status. For example,
     * the owner may be momentarily {@code null} even if there are
     * threads trying to acquire the lock but have not yet done so.
     * This method is designed to facilitate construction of
     * subclasses that provide more extensive lock monitoring
     * facilities.
     *
     * @return the owner, or {@code null} if not owned
     */
    protected Thread getOwner() {
        return sync.getOwner();
    }

    /**
     * Queries whether any threads are waiting to acquire this lock. Note that
     * because cancellations may occur at any time, a {@code true}
     * return does not guarantee that any other thread will ever
     * acquire this lock.  This method is designed primarily for use in
     * monitoring of the system state.
     *
     * @return {@code true} if there may be other threads waiting to
     *         acquire the lock
     */
    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }

    /**
     * Queries whether the given thread is waiting to acquire this
     * lock. Note that because cancellations may occur at any time, a
     * {@code true} return does not guarantee that this thread
     * will ever acquire this lock.  This method is designed primarily for use
     * in monitoring of the system state.
     *
     * @param thread the thread
     * @return {@code true} if the given thread is queued waiting for this lock
     * @throws NullPointerException if the thread is null
     */
    public final boolean hasQueuedThread(Thread thread) {
        return sync.isQueued(thread);
    }

    /**
     * Returns an estimate of the number of threads waiting to
     * acquire this lock.  The value is only an estimate because the number of
     * threads may change dynamically while this method traverses
     * internal data structures.  This method is designed for use in
     * monitoring of the system state, not for synchronization
     * control.
     *
     * @return the estimated number of threads waiting for this lock
     */
    public final int getQueueLength() {
        return sync.getQueueLength();
    }

    /**
     * Returns a collection containing threads that may be waiting to
     * acquire this lock.  Because the actual set of threads may change
     * dynamically while constructing this result, the returned
     * collection is only a best-effort estimate.  The elements of the
     * returned collection are in no particular order.  This method is
     * designed to facilitate construction of subclasses that provide
     * more extensive monitoring facilities.
     *
     * @return the collection of threads
     */
    protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }

    /**
     * Queries whether any threads are waiting on the given condition
     * associated with this lock. Note that because timeouts and
     * interrupts may occur at any time, a {@code true} return does
     * not guarantee that a future {@code signal} will awaken any
     * threads.  This method is designed primarily for use in
     * monitoring of the system state.
     *
     * @param condition the condition
     * @return {@code true} if there are any waiting threads
     * @throws IllegalMonitorStateException if this lock is not held
     * @throws IllegalArgumentException if the given condition is
     *         not associated with this lock
     * @throws NullPointerException if the condition is null
     */
    public boolean hasWaiters(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

    /**
     * Returns an estimate of the number of threads waiting on the
     * given condition associated with this lock. Note that because
     * timeouts and interrupts may occur at any time, the estimate
     * serves only as an upper bound on the actual number of waiters.
     * This method is designed for use in monitoring of the system
     * state, not for synchronization control.
     *
     * @param condition the condition
     * @return the estimated number of waiting threads
     * @throws IllegalMonitorStateException if this lock is not held
     * @throws IllegalArgumentException if the given condition is
     *         not associated with this lock
     * @throws NullPointerException if the condition is null
     */
    public int getWaitQueueLength(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

    /**
     *
     * @param condition the condition
     * @return the collection of threads
     * @throws IllegalMonitorStateException if this lock is not held
     * @throws IllegalArgumentException if the given condition is
     *         not associated with this lock
     * @throws NullPointerException if the condition is null
     */
    protected Collection<Thread> getWaitingThreads(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

    /**
     *toString方法
     * @return a string identifying this lock, as well as its lock state
     */
    public String toString() {
        Thread o = sync.getOwner();
        return super.toString() + ((o == null) ?
                                   "[Unlocked]" :
                                   "[Locked by thread " + o.getName() + "]");
    }
}

2.1 类的继承关系

public class ReentrantLock implements Lock, java.io.Serializable {

ReentrantLock实现了Lock接口,Lock接口中定义了lock与unlock相关操作,并且还存在newCondition方法,表示生成一个条件。

package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;

public interface Lock {


    void lock();


    void lockInterruptibly() throws InterruptedException;

    boolean tryLock();


    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;


    void unlock();


    Condition newCondition();
}

2.2 类的内部类

ReentrantLock总共存在Sync、NonfairSync、FairSync三个内部类,并且三个内部类是紧密相关的。NonfairSync与FairSync类继承自Sync类,Sync类继承自AbstractQueuedSynchronizer抽象类。下面逐个进行分析。

2.2.1Sync类

    /**
     * 实现锁的机制是通过Sync进行操作的。Sync类是继承AbstractQueuedSynchronizer类的。这也就表明ReentrantLock是基于AQS的实现的。
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
	/* 序列号 */
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * 获取锁
         */
        abstract void lock();

        /**
         *  非公平方式获取
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {// 表示没有线程正在竞争该锁
                if (compareAndSetState(0, acquires)) {// 比较并设置状态成功,状态0表示锁没有被占用
					// 设置当前线程独占
                    setExclusiveOwnerThread(current);
					// 成功
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {// 当前线程拥有该锁
			    // 增加重入次数
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
				// 设置状态
                setState(nextc);
                return true;
            }
            return false;
        }

        /**
         *   试图在共享模式下获取对象状态,此方法应该查询是否允许它在共享模式下获取对象状态,如果允许,则获取它
         */		  
        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;
        }
        /**
         *判断资源是否被当前线程占有
         */	
        protected final boolean isHeldExclusively() {

            return getExclusiveOwnerThread() == Thread.currentThread();
        }
        /**
         * 新生一个条件
         */	
        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class
        /**
         *  返回资源的占用线程
         */	
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
        /**
         *  返回资源的占用线程
         */	
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
        /**
         * 资源是否被占用
         */	
        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * 自定义反序列化逻辑
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

Sync类存在如下方法和作用如下。

sync方法

方法作用
lock()锁定,并没有实现,留给子类实现
nonfairTryAcquire(int acquires)非公平方式 获取
tryRelease(int releases)试图在共享模式下获取对象状态,此方法应该查询是否允许它在共享模式下获取对象状态,如果允许,则获取它
isHeldExclusively()判断资源是否被当前线程占有
newCondition()新生一个条件
getOwner()返回资源占用线程
getHoldCount()返回资源占用线程的状态
isLocked()资源是否被占用
readObject(java.io.ObjectInputStream s)自定义反序列化逻辑

2.2.2NonfairSync类

NonfairSync类继承了Sync类,表示采用非公平策略获取锁,其实现了Sync类中抽象的lock方法。

    /**
     *  非公平锁的同步器
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         *  获得锁
         */
        final void lock() {
            if (compareAndSetState(0, 1)) //比较并设置状态成功,状态0表示锁没有被占用
	       // 把当前线程设置独占了锁
                setExclusiveOwnerThread(Thread.currentThread());
            else// 锁已经被占用,或者set失败
	     // 以独占模式获取对象,忽略中断
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

从lock方法的源码可知,每一次都尝试获取锁,而并不会按照公平等待的原则进行等待,让等待时间最久的线程获得锁。

2.2.3FairSync

FairSync类也继承了Sync类,表示采用公平策略获取锁,其实现了Sync类中的抽象lock方法

    /**
     * 实现公平锁的同步器
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
		//实现父类的抽象获取锁的方法
        final void lock() {
	  //调用acquire让线程去同步队列排队以独占模式获取对象,忽略中断
            acquire(1);
        }

        /**
         * 尝试公平获取锁
         */
        protected final boolean tryAcquire(int acquires) {
	   // 获取当前线程
            final Thread current = Thread.currentThread();
	    // 获取当前同步状态
            int c = getState();
	    //如果同步状态0则表示锁没被占用
            if (c == 0) {
		//判断同步队列是否有前继结点
                if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {// 不存在已经等待更久的线程并且比较并且设置状态成功
                     // 设置当前线程独占
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {// 状态不为0,即资源已经被线程占据
	        // 下一个状态
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
		// 设置状态如果是当前线程持有锁就直接修改同步状态
                setState(nextc);
                return true;
            }
            return false;
        }
    }

跟踪lock方法的源码可知,当资源空闲时,它总是会先判断sync队列(AbstractQueuedSynchronizer中的数据结构)是否有等待时间更长的线程,如果存在,则将该线程加入到等待队列的尾部,实现了公平获取原则。

其中,FairSync类的lock的方法调用如下,只给出了主要的方法。

FairSync:lock----->AQS:acquire------>FairSync:tryAcquire------>AQS:AddWaiter----->AQS:acquireQueued

说明:可以看出只要资源被其他线程占用,该线程就会添加到sync queue中的尾部,而不会先尝试获取资源。这也是和Nonfair最大的区别,Nonfair每一次都会尝试去获取资源,如果此时该资源恰好被释放,则会被当前线程获取,这就造成了不公平的现象,当获取不成功,再加入队列尾部。

2.2.4 ReentrantLock的属性

    /* 序列号 */
    private static final long serialVersionUID = 7373984872572414699L;
    /** 同步队列 */
    private final Sync sync;

说明:ReentrantLock类的sync非常重要,对ReentrantLock类的操作大部分都直接转化为对Sync和AbstractQueuedSynchronizer类的操作。

2.2.5 ReentrantLock类的构造方法

默认的无参构造方法

    /**
     * ReentrantLock默认的无参构造方法
     */
    public ReentrantLock() {
		 // 默认非公平策略
        sync = new NonfairSync();
    }

带一个参数的构造方法

    /**
     * 可以传递参数确定采用公平策略或者是非公平策略,参数为true表示公平策略,否则,采用非公平策略
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

2.2.6 ReentrantLock核心方法

通过分析ReentrantLock的源码,可知对其操作都转化为对Sync对象的操作,由于Sync继承了AQS,所以基本上都可以转化为对AQS的操作。如将ReentrantLock的lock函数转化为对Sync的lock函数的调用,而具体会根据采用的策略(如公平策略或者非公平策略)的不同而调用到Sync的不同子类。

  所以可知,在ReentrantLock的背后,是AQS对其服务提供了支持。

三.使用实例

package cn.itcast_01;

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

public class SellTicketRunnable implements Runnable {
	private int ticketNum=100;
	Lock lock=new ReentrantLock();
	@Override
	public void run() {
		while(true){
			//加锁
			lock.lock();
			if(ticketNum>0){
				//为了模拟更真实的场景我们稍作休息
				
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticketNum--)+"张票");
			}
			//释放锁
			lock.unlock();
		}
	}

}

测试类:

package cn.itcast_01;

/**
 * 实现Runnable接口的方式实现
 * @author jack
 *
 *通过加入延迟后,就产生了两个问题
 *1.同票多次
 *2.负票 
 */
public class SellTicketDemo {

	public static void main(String[] args) {
		//创建资源对象
		SellTicketRunnable st=new SellTicketRunnable();
		
		//创建三个线程对象
		Thread t1=new Thread(st,"窗口1");
		Thread t2=new Thread(st,"窗口2");
		Thread t3=new Thread(st,"窗口3");
		

		t1.start();
		t2.start();
		t3.start();
	}

}

运行结果:

窗口1正在出售第100张票
窗口1正在出售第99张票
窗口1正在出售第98张票
窗口1正在出售第97张票
窗口3正在出售第96张票
窗口2正在出售第95张票
窗口1正在出售第94张票
窗口1正在出售第93张票
窗口3正在出售第92张票
窗口3正在出售第91张票
窗口2正在出售第90张票
窗口2正在出售第89张票
窗口2正在出售第88张票
窗口1正在出售第87张票
窗口3正在出售第86张票
窗口3正在出售第85张票
窗口2正在出售第84张票
窗口1正在出售第83张票
窗口1正在出售第82张票
窗口3正在出售第81张票
窗口3正在出售第80张票
窗口3正在出售第79张票
窗口3正在出售第78张票
窗口3正在出售第77张票
窗口3正在出售第76张票
窗口3正在出售第75张票
窗口3正在出售第74张票
窗口2正在出售第73张票
窗口1正在出售第72张票
窗口3正在出售第71张票
窗口3正在出售第70张票
窗口3正在出售第69张票
窗口3正在出售第68张票
窗口3正在出售第67张票
窗口3正在出售第66张票
窗口3正在出售第65张票
窗口2正在出售第64张票
窗口2正在出售第63张票
窗口2正在出售第62张票
窗口1正在出售第61张票
窗口1正在出售第60张票
窗口3正在出售第59张票
窗口2正在出售第58张票
窗口1正在出售第57张票
窗口1正在出售第56张票
窗口3正在出售第55张票
窗口3正在出售第54张票
窗口3正在出售第53张票
窗口3正在出售第52张票
窗口2正在出售第51张票
窗口2正在出售第50张票
窗口2正在出售第49张票
窗口2正在出售第48张票
窗口1正在出售第47张票
窗口3正在出售第46张票
窗口3正在出售第45张票
窗口3正在出售第44张票
窗口3正在出售第43张票
窗口3正在出售第42张票
窗口3正在出售第41张票
窗口2正在出售第40张票
窗口1正在出售第39张票
窗口1正在出售第38张票
窗口1正在出售第37张票
窗口1正在出售第36张票
窗口1正在出售第35张票
窗口1正在出售第34张票
窗口1正在出售第33张票
窗口1正在出售第32张票
窗口3正在出售第31张票
窗口3正在出售第30张票
窗口3正在出售第29张票
窗口3正在出售第28张票
窗口3正在出售第27张票
窗口3正在出售第26张票
窗口2正在出售第25张票
窗口2正在出售第24张票
窗口1正在出售第23张票
窗口1正在出售第22张票
窗口1正在出售第21张票
窗口1正在出售第20张票
窗口1正在出售第19张票
窗口3正在出售第18张票
窗口3正在出售第17张票
窗口3正在出售第16张票
窗口2正在出售第15张票
窗口2正在出售第14张票
窗口1正在出售第13张票
窗口1正在出售第12张票
窗口1正在出售第11张票
窗口1正在出售第10张票
窗口3正在出售第9张票
窗口3正在出售第8张票
窗口3正在出售第7张票
窗口3正在出售第6张票
窗口2正在出售第5张票
窗口1正在出售第4张票
窗口1正在出售第3张票
窗口1正在出售第2张票
窗口1正在出售第1张票

代码改进版:

package cn.itcast_01;

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

public class SellTicketRunnable implements Runnable {
	private int ticketNum=100;
	Lock lock=new ReentrantLock();
	@Override
	public void run() {
		while(true){
			
			try{
				//加锁
				lock.lock();
				if(ticketNum>0){
					//为了模拟更真实的场景我们稍作休息
					
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticketNum--)+"张票");
				}
			}finally{
				//释放锁
				lock.unlock();
			}	
		}
	}

}

    原文作者:JUC
    原文地址: https://blog.csdn.net/qq_35558797/article/details/80059180
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞