synchronized

在实际开发中,多线程是很常见的,既然有多线程必然会出现并发,并发操作同一资源时,比如多个线程写入同一文件,就需要进行同步,也就是同一时间只能一个线程写入同一文件。在java中synchronized关键字就是同步用的。

由于没有系统学习过多线程编程,只能按照自己的理解和看到源码的记录一下synchronized的使用场景。

(1)一种使用情况就是,一个线程执行完另一个线程才能执行,只要synchronized关键字就可以实现,两个线程是不需要通信的。

(2)还有一种使用情况是,一个线程执行时,发现条件不满足时线程进入等待阻塞状态,释放锁,另一个线程获取锁可以执行,根据情况唤醒等待的线程进行执行。需要synchronized和Object的wait/notify配合实现,两个线程进行了通信。下面的HandlerThread就是这种场景。

synchronized同步是jvm实现的,底层实现是采用了锁机制。

在学习HandlerThread源码的时候,看到了线程同步的用法,借此机会粗浅的学习一下,为以后有机会深入学习的时候做个铺垫,要不然每次这块知识都是0基础,一想起就心虚。

@Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    
    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason isAlive() returns false, this method will return null. If this thread
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

上面就是同步的标准用法,而且用到了wait和notifyAll方法。synchronized关键字,能让调用方法的当前线程拥有this对象(HandlerThread)的监控锁(monitor lock)。wait/notify/notifyAll这些方法必须要和锁机制一块使用,标准解释是:The current thread must own this object's monitor. 解释一下上面的代码

在实际开发中,HandlerThread的getLooper()方法是在主线程中调用的,也就是说,在主线程中调用getLooper方法执行到sychronized(this)时,主线程申请拥有HandlerThread对象监控锁,如果上锁成功,循环判断线程存活并且mLooper为null,执行wait方法,此时主线程释放了cpu资源和锁资源,进入WAITING状态。run方法是线程的执行方法,执行到sychronized(this)时,HandlerThread线程申请拥有HandlerThread对象的监控锁(俗称上锁),如果没有其它线程对HandlerThread对象的监控,那么就可以上锁成功,执行了notifyAll,退出了同步块。主线程会被唤醒,继续执行。

我们看看wait和notifyAll方法,这两个方法是在Object中定义的,我们看看注释

/**
     * Causes the current thread to wait until another thread invokes the
     * {@link java.lang.Object#notify()} method or the
     * {@link java.lang.Object#notifyAll()} method for this object.
     * In other words, this method behaves exactly as if it simply
     * performs the call {@code wait(0)}.
     * <p>
     * The current thread must own this object's monitor. The thread
     * releases ownership of this monitor and waits until another thread
     * notifies threads waiting on this object's monitor to wake up
     * either through a call to the {@code notify} method or the
     * {@code notifyAll} method. The thread then waits until it can
     * re-obtain ownership of the monitor and resumes execution.
     * <p>
     * As in the one argument version, interrupts(中断) and spurious(欺骗的、伪造的) wakeups are
     * possible, and this method should always be used in a loop:
     * <pre>
     *     synchronized (obj) {
     *         while (<condition does not hold>)
     *             obj.wait();
     *         ... // Perform action appropriate to condition
     *     }
     * </pre>
     * This method should only be called by a thread that is the owner
     * of this object's monitor.(这个方法仅仅被拥有对象监控的的线程调用) See the {@code notify} method for a
     * description of the ways in which a thread can become the owner of
     * a monitor.
     *
     * @throws  IllegalMonitorStateException  if the current thread is not
     *               the owner of the object's monitor.
     * @throws  InterruptedException if any thread interrupted the
     *             current thread before or while the current thread
     *             was waiting for a notification.  The <i>interrupted
     *             status</i> of the current thread is cleared when
     *             this exception is thrown.
     * @see        java.lang.Object#notify()
     * @see        java.lang.Object#notifyAll()
     */
    @FastNative
    public final native void wait() throws InterruptedException;

根据wait的注释,我们知道,要调用某个对象A的wait方法,首先当前线程必须拥有A的监控,也就是上锁A。调用wait之后,当前线程处于了等待状态,释放了锁,也就是释放了对A的监控,其它线程可以对A上锁了。线程间对A上锁是竞争关系。

wait还有其它两个重载方法,用于设置等待时间,0的话表示永远等待,和无参的wait方法一个意思,这点得注意一下。

interrupts and spurious wakeups are possible, and this method should always be used in a loop. 中断和伪唤醒是可能的,所以wait方法应该在循环中使用。

线程调用了对象方法interrupt,会给线程设置一个中断标记,如果此线程之前因为wait处于WAITING状态时,会catch到InterruptedException,中断标记会被清除,catch后可以进行一些处理。

/**
     * Wakes up a single thread that is waiting on this object's
     * monitor. If any threads are waiting on this object, one of them
     * is chosen to be awakened. The choice is arbitrary (随意的)and occurs at
     * the discretion (考虑周到、自由裁量)of the implementation. A thread waits on an object's
     * monitor by calling one of the {@code wait} methods.
     * <p>
     * The awakened thread will not be able to proceed until the current
     * thread relinquishes(放弃) the lock on this object. The awakened thread will
     * compete in the usual manner with any other threads that might be
     * actively(积极地) competing(竞争) to synchronize on this object; for example, the
     * awakened thread enjoys no reliable privilege(特权) or disadvantage in being
     * the next thread to lock this object.
     * <p>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. A thread becomes the owner of the
     * object's monitor in one of three ways:
     * <ul>
     * <li>By executing a synchronized instance method of that object.
     * <li>By executing the body of a {@code synchronized} statement
     *     that synchronizes on the object.
     * <li>For objects of type {@code Class,} by executing a
     *     synchronized static method of that class.
     * </ul>
     * <p>
     * Only one thread at a time can own an object's monitor.
     *
     * @throws  IllegalMonitorStateException  if the current thread is not
     *               the owner of this object's monitor.
     * @see        java.lang.Object#notifyAll()
     * @see        java.lang.Object#wait()
     */
    @FastNative
    public final native void notify();

从注释,我们知道,notify的调用前提和wait是一样的。如果有多个线程处于等待状态是,notify只会唤醒一个线程,这个被唤醒的线程是随意的,由实现来决定的。被唤醒的线程也是需要重新和其它线程竞争对A的监控,它们直接是平等的。

notifyAll会唤醒,对A对象监控后进入等待状态的线程,这些线程会对A监控进行竞争。

synchronized提供的锁很简单,使用比较方便,上锁和释放锁是jvm帮助完成的,这种锁属于可重入锁,而且阻塞是不能被中断的。所以,使用synchronized不能使用非重入锁,而且也不具备查询是否有锁的能力。synchronized使用不够灵活。java引入了AQS锁。 

AbstractQueuedSynchronizer(AQS)

具体原理细节没有看源码,不过使用很简单,只要子类重写几个方法就可以了

AQS其实上锁的时候,使用CAS更新state表示有没有上锁,上锁的话记录一下获取锁的线程。jvm使用sychronized估计也是这个思想。

AQS tryAcquire方法尝试去获取锁,获取到返回true,获取不到返回false。这个是需要子类重写决定的。acquire方法里面调用了tryAcquire方法,获取不到锁会阻塞。

可以学习一下ThreadPoolExecutor中Worker AQS的使用

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        /**
         * This class will never be serialized, but we provide a
         * serialVersionUID to suppress a javac warning.
         */
        private static final long serialVersionUID = 6138294804551838833L;

        /** Thread this worker is running in.  Null if factory fails. */
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
        /** Per-thread task counter */
        volatile long completedTasks;

        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        /** Delegates main run loop to outer runWorker. */
        public void run() {
            runWorker(this);
        }

        // Lock methods
        //
        // The value 0 represents the unlocked state.
        // The value 1 represents the locked state.

        protected boolean isHeldExclusively() {
            return getState() != 0;
        }

        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        public void lock()        { acquire(1); }
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }

        void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
    }