Android: 线程相关知识点

快速切换到主线程更新UI的三种方法

  • Activity.runOnUiThread(Runnable)
    把更新UI的代码创建在Runnable中,并传给 Activity.runOnUiThread()如果当前线程是UI线程,那么会立即执行;反之,会调用UI线程handler.post()将其放入UI线程的消息队列中
new Thread(){
        public void run() {
        //此时在子线程
                ((MainActivity) context).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                        //此时已经回到主线程,注意要强转到主线程的MainActivity
                        }
                });
        };
}.start();
  • View.post(Runnable)
    如果View已经attach到Window,直接调用UI线程的Handler发送Runnable到MessageQueue
    如果View还未attach到Window,将Runnable放入ViewRootImpl的RunQueue中,RunQueue会实现延迟执行Runnable任务,并且Runnable最终不会被加入到MessageQueue里,也不会被Looper执行,而是等到ViewRootImpl的下一个performTraversals时候,把RunQueue里的所有Runnable都拿出来并执行,接着清空RunQueue
mTextView.post(new Runnable() {
        @Override
        //可快速更新该view
        public void run() {
                mTextView.setText("xxx");
        //也可以更新其他view
        mIageView.setBackgroundResource(R.drawable.icon);
        }
});

还有View.postDelayed(Runnable, long),其中long表示延迟的毫秒数,示例如下。

//5秒倒计时跳转到下个页面
index=5;
mTextView.postDelayed(new Runnable() {
        @Override
        public void run() {
                mTextView.setText("" + index);
                index--;
                if (index == 0) {
                        Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
                } else {
                        mTextView.postDelayed(this, 1000);
                }
        }
}, 1000);

c.Handler.post(Runnable)&Handler.post(Runnable,long)

//假设在子线程,需要获取主线程的Looper和Queue
new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
                //此时已经回到主线程
        }
});

当然还可以用AsyncTask、IntentService、HandlerThread,还有许多开源框架。

Thread阻塞方式:

  • sleep():
    进入休眠状态,但持有对象的锁旗标,休眠结束后,从阻塞状态——》就绪状态。

  • yield():
    进入休眠状态,也持有对象的锁旗标和sleep相似,但是不能控制时间,也有可能刚放弃运行就重新获取运行权。

  • wait():
    释放锁旗标,进入wait池,等到另一个线程对等待的对象调用notify()或notifyall()后,等待线程才会从wait池进入lock池(从一种阻塞状态进入另一种阻塞状态)。

  • synchornized未获取对象的锁旗标
    进入该对象lock池,等待锁旗标被归还,该对象的lock池中的线程才会获得锁旗标进入就绪状态。

  • Threa中的isAlive(),与线程状态有关。
    线程启动后,并且在run()方法没有结束前,isAlive返回true。
    所以如果返回false,就知道该线程处于刚被创建还尚未处于strat阶段,或者已处于dead状态。
    如果返回true,该线程就处于其生命周期中(运行或阻塞)。

线程状态流程图

java_thread_state.png
  • NEW:创建状态,线程创建之后,但是还未启动。
  • RUNNABLE:运行状态,处于运行状态的线程,但有可能处于等待状态,例如等待CPU、IO等。
  • WAITING:等待状态,一般是调用了wait()、join()、LockSupport.spark()等方法。
  • TIMED_WAITING:超时等待状态,也就是带时间的等待状态。一般是调用了wait(time)、join(time)、LockSupport.sparkNanos()、LockSupport.sparkUnit()等方法。
  • BLOCKED:阻塞状态,等待锁的释放,例如调用了synchronized增加了锁。
  • TERMINATED:终止状态,一般是线程完成任务后退出或者异常终止。

NEW、WAITING、TIMED_WAITING都比较好理解,我们重点说一说RUNNABLE运行态和BLOCKED阻塞态。

线程进入RUNNABLE运行态一般分为五种情况:

  • 线程调用sleep(time)后查出了休眠时间
  • 线程调用的阻塞IO已经返回,阻塞方法执行完毕
  • 线程成功的获取了资源锁
  • 线程正在等待某个通知,成功的获得了其他线程发出的通知
  • 线程处于挂起状态,然后调用了resume()恢复方法,解除了挂起。

线程进入BLOCKED阻塞态一般也分为五种情况:

  • 线程调用sleep()方法主动放弃占有的资源
  • 线程调用了阻塞式IO的方法,在该方法返回前,该线程被阻塞。
  • 线程视图获得一个资源锁,但是该资源锁正被其他线程锁持有。
  • 线程正在等待某个通知
  • 线程调度器调用suspend()方法将该线程挂起

我们再来看看和线程状态相关的一些方法。

  • sleep()方法让当前正在执行的线程在指定时间内暂停执行,正在执行的线程可以通过Thread.currentThread()方法获取。
  • yield()方法放弃线程持有的CPU资源,将其让给其他任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。
  • wait()方法是当前执行代码的线程进行等待,将当前线程放入预执行队列,并在wait()所在的代码处停止执行,知道接到通知或者被中断为止。该方法可以使得调用该方法的线程释放共享资源的锁, 然后从运行状态退出,进入等待队列,直到再次被唤醒。该方法只能在同步代码块里调用,否则会抛出IllegalMonitorStateException异常。wait(long millis)方法等待某一段时间内是否有线程对锁进行唤醒,如果超过了这个时间则自动唤醒。
  • notify()方法用来通知那些可能等待该对象的对象锁的其他线程,该方法可以随机唤醒等待队列中等同一共享资源的一个线程,并使该线程退出等待队列,进入可运行状态。
  • notifyAll()方法可以是所有正在等待队列中等待同一共享资源的全部线程从等待状态退出,进入可运行状态,一般会是优先级高的线程先执行,但是根据虚拟机的实现不同,也有可能是随机执行。
  • join()方法可以让调用它的线程正常执行完成后,再去执行该线程后面的代码,它具有让线程排队的作用。

你可能感兴趣的:(Android: 线程相关知识点)