Thread调度的四种方法

目录

前言

1.wait(),notify(),notifyAll()

2.ReentrantLock代替wait,notify,notifyAll方法

3.Thread的join方法

4.CountDownLatch的await,counDown方法

Tips


前言

差不多两年前吧,校招面试的时候,被网易的面试官问过线程怎么调度,忘记以前怎么回答的去了,没想到现在居然想写一篇文章来记录下。

文章,一为温故而知新,二若是可以帮助到别人,也是我的荣幸。

因本人能力有限,若有错误之处,麻烦指出。如果觉得有可取之处,麻烦点赞支持一下。 😊

1.wait(),notify(),notifyAll()

Object类的wait(),notify(),notifyAll()应该是我目前在源码中见过的最多的线程调度的方法,如HandlerThread,SharedPrefercesImpl等。先写个简单的例子。

public class MainActivity3 extends Activity {
    private static final String TAG = "MainActivity3";
    private final Object object = new Object();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);

        new Thread(() -> {
            Log.i(TAG, "thread start ");
            try {
                synchronized (object) {
                    object.wait();

                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.i(TAG, "thread end");
        }).start();

        Log.i(TAG, "sleep");
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        synchronized (object) {
            Log.i(TAG, "notify");
            object.notify();
        }

    }
}



2021-03-27 22:00:33.012 25597-25597/com.example.canvasbitmap I/MainActivity3: sleep
2021-03-27 22:00:33.013 25597-25622/com.example.canvasbitmap I/MainActivity3: thread start 
2021-03-27 22:00:33.313 25597-25597/com.example.canvasbitmap I/MainActivity3: notify
2021-03-27 22:00:33.313 25597-25622/com.example.canvasbitmap I/MainActivity3: thread end

从日志可以看出来,在子线程调用了object.wait()方法之后,子线程停止了运行,直到object.notity()方法在主线程调用之后,子线程才恢复运行。

源码注释:

 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.

 public final void wait() throws InterruptedException {
        wait(0);
    }

这是wait方法的源码注释,简单来说就是当前线程调用了object.wait方法后,要等到另外一个线程调用object.notify方法或者notifyAll方法,才能够继续执行。

并且在调用wait方法时,必须持有object的锁,直到其他线程通知它,等待获取object的锁,才会释放,等待它再次获取object的锁时,它才会接着执行。

notify和notifyAll方法区别是,notify只能唤醒一个执行了object.wait方法的线程,而notifyAll能够唤醒所有执行了object.wait方法的线程,注意必须是同一个object。 

 

HandleThread的使用

简单说明一下,一般使用HandlerThread,是为了创建一个拥有Loop的子线程,然后通过该Loop创建的Handler处理的消息都是在该子线程执行。

HandlerThread handlerThread = new HandlerThread("handlerThread-io");
handlerThread.start();
Handler ioHandler = new Handler(handlerThread.getLooper());

HandlerThread调用它的start方法是为了执行下面run方法中的逻辑,也就是创建属于子线程的Loop,随即通过handlerThread调用getLooper获取Loop,并通过该Loop创建Handler。

在这几步操作过程有个时序问题,getLooper的时候,run方法不一定执行完了,毕竟getLoop方法和run方法运行在两个线程。

于是,在getLoop时,进行判断,若是Loop还没创建成功,则调用wait方法让该线程等待Loop的创建,而在Loop创建成功后,则调用notifyAll方法,唤醒wait的线程,返回Loop,从而保证getLoop方法能够返回Loop。

public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    

    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;
    }

kotlin中没有object类,也就意味着没有wait,notify,和notifyAll方法,那该怎么办呢?下面介绍另外一个线程调度的方法,ReentrantLock的使用。

2.ReentrantLock代替wait,notify,notifyAll方法

示例代码如下

class MainActivity : Activity() {

    private val lock = ReentrantLock()
    private val condition: Condition = lock.newCondition()

    companion object {
        private const val TAG = "MainActivity"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        Thread(Runnable {
            lock.withLock {
                Log.i(TAG, "start")
                condition.await()
                Log.i(TAG, "end")
            }
        }).start()

        Log.i(TAG, "sleep")

        Thread.sleep(300)

        Log.i(TAG, "signal")
        lock.withLock {
            condition.signalAll()
        }
    }
}


2021-03-27 22:19:00.988 27349-27349/com.example.canvasbitmap I/MainActivity: sleep
2021-03-27 22:19:00.988 27349-27383/com.example.canvasbitmap I/MainActivity: start
2021-03-27 22:19:01.289 27349-27349/com.example.canvasbitmap I/MainActivity: signal
2021-03-27 22:19:01.290 27349-27383/com.example.canvasbitmap I/MainActivity: end

condition的await,sigal,sigalAll方法依次等价于object的wait,notify,notifyAll方法

3.Thread的join方法

先看下源码介绍

/**
     * Waits for this thread to die. 等待这个线程死亡
     *
     * <p> An invocation of this method behaves in exactly the same
     * way as the invocation
     *
     * <blockquote>
     * {@linkplain #join(long) join}{@code (0)}
     * </blockquote>
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final void join() throws InterruptedException {
        join(0);
    }

 Waits for this thread to die. 等待这个线程死亡,这句话的意思是,如果你在其他线程调用了这个线程的thread.join方法,那么一直要等到这个线程死亡才能继续往下走。

代码示例

public class MainActivity5 extends Activity {

    private static final String TAG = "MainActivity5";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main5);

        Thread thread = new Thread(() -> {
            Log.i(TAG, "thread start");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.i(TAG, "thread end");
        });

        thread.start();

        try {
            Log.i(TAG, "join");
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Log.i(TAG, "thread is alive ==" + thread.isAlive());

    }
}


2021-03-27 21:08:01.830 19430-19430/com.example.canvasbitmap I/MainActivity5: join
2021-03-27 21:08:01.830 19430-19451/com.example.canvasbitmap I/MainActivity5: thread start
2021-03-27 21:08:02.831 19430-19451/com.example.canvasbitmap I/MainActivity5: thread end
2021-03-27 21:08:02.832 19430-19430/com.example.canvasbitmap I/MainActivity5: thread is alive ==false

join方法的具体实现调用的是object方法的wait方法。

4.CountDownLatch的await,counDown方法

CountDownLatch相较于其他线程调度的方法的优势在于,它可以控制一个线程等待多个线程执行完。

public class MainActivity6 extends Activity {
    private static final String TAG = "MainActivity6";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main6);

        CountDownLatch latch = new CountDownLatch(3);

        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "thread-1 count = " + latch.getCount());
                latch.countDown();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "thread-2 count = " + latch.getCount());
                latch.countDown();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "thread-3 count = " + latch.getCount());
                latch.countDown();
            }
        }).start();

        try {
            Log.i(TAG, "await");
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.i(TAG, "currentThread count = " + latch.getCount());
        Log.i(TAG, "end");

    }
}


2021-03-27 21:32:31.392 22593-22593/com.example.canvasbitmap I/MainActivity6: await
2021-03-27 21:32:31.393 22593-22616/com.example.canvasbitmap I/MainActivity6: thread-1 count = 3
2021-03-27 21:32:31.393 22593-22617/com.example.canvasbitmap I/MainActivity6: thread-2 count = 3
2021-03-27 21:32:31.394 22593-22618/com.example.canvasbitmap I/MainActivity6: thread-3 count = 1
2021-03-27 21:32:31.395 22593-22593/com.example.canvasbitmap I/MainActivity6: currentThread count = 0
2021-03-27 21:32:31.395 22593-22593/com.example.canvasbitmap I/MainActivity6: end

CountDownLatch传入的数值为3,也就是意味着,需要调用countDown方法3次,才能够使await的线程被唤醒。

Tips

object.wait方法,thread的join方法和ReentrantLock的wait方法都可以设置等待时间,要是超过这个时间还没被唤醒,那么就是自动被唤醒。

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值