安卓线程(基础概念与方法)

Android开发艺术探索里这样讲解了线程与进程:按照操作系统的描述,线程是CPU调度的最小单元,同时线程是一种有限的系统资源。而进程一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用。 一个进程可以包含多个线程,因此进程和线程是包含与被包含的关系。简单情况下,一个进程可以只有一个线程, 即主线程,在Android里面主线程也叫UI线程,在UI线程里才能操作界面。

线程的创建有两种方式: 1、继承Thread对象,重写run方法 2、继承Runnable接口,实现run方法

1:

 private class MyThread extends Thread {

        public MyThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            super.run();
        }
    }

创建线程并执行任务 

 MyThread myThread = new MyThread("MyThread");
        myThread.start();

2:

private class MyRunnable implements Runnable {
        @Override
        public void run() {

        }
    }
创建线程并执行任务
MyRunnable myRunnable = new MyRunnable();
        Thread runnableThread = new Thread(myRunnable, "MyRunnable");
        runnableThread.start();

线程的生命周期
新建状态
用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己
的内存空间。


就绪状态
通过调用start方法进入就绪状态,处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,
处于线程就绪队列,等待系统为其分配CPU。

运行状态
我觉得可以理解为当线程得到CPU资源的时候,线程就处于运行状态

阻塞状态
处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。

死亡状态
当线程的run()方法执行完,或者由于一些特殊原因如资源不足等被强制性地终止。

线程中的主要方法   run()  start()   sleep()  wait()

1、run()方法和start()方法的区别
start()方法是用来启动一个线程,然后就返回主线程,那么这个启动的线程不会马上执行,会放到队列中等待cup调度,虽然叫队列,但是他不是队列,因为线程并不是按顺序执行的,只有当线程被cpu调度时才会调用run()方法。查看start()方法的源码我们发现被关键字synchronized修饰,即为了防止被多次启动的一个同步操作。那么这里需要注意的是,start()方法是不能调用两次的,我们可以这样理解,当一个线程调用了start方法之后,那么他能是新建的状态,同样可能是其他非NEW的状态,那么我们再调用得时候必然会存在问题,系统会给出java.lang.IllegalStateException的异常;对于启动一个线程而言,我们只能调用start()方法,不能直接调用run()方法,先打印看结果:

        MyRunnable myRunnable = new MyRunnable();
        Thread runnableThread = new Thread(myRunnable, "MyRunnable");
        runnableThread.run();
        runnableThread.start();

        private class MyRunnable implements Runnable {
            @Override
            public void run() {
                Log.e("threadName:", Thread.currentThread().getName());
            }
        }

那么两次打印的结果显示,直接调用run方法实际还在主线程

javastudy.come.javastudy E/threadName:: main
javastudy.come.javastudy E/threadName:: MyRunnable

至于为什么我们查看start()的源码

 public synchronized void start() {

        if (threadStatus != 0 || started)
            throw new IllegalThreadStateException();
        group.add(this);

        started = false;
        try {
            nativeCreate(this, stackSize, daemon);
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {

            }
        }
    }

删除多余注释,我们可以看到   nativeCreate(this, stackSize, daemon)方法,这是一个c的代码,我们就可以理解为是创建线程的,而当我们直接调用run方法的时候

  @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

看到是走这里,这个target我们可以看到他是一个Runable,Runable是一个接口,里面唯一的方法是run();这样和调用普通的接口里面的方法没有什么区别了,所以依旧还是在主线程。

2、sleep()与wait()方法

首先说下二者的区别,sleep()属于thread而wait()属于Object,打开源码发现

      synchronized (lock) {
            while (true) {
                sleep(lock, millis, nanos);

                long now = System.nanoTime();
                long elapsed = now - start;

                if (elapsed >= duration) {
                    break;
                }

                duration -= elapsed;
                start = now;
                millis = duration / NANOS_PER_MILLI;
                nanos = (int) (duration % NANOS_PER_MILLI);
            }
        }

这是类似于计时的功能,睡的时间到了,跳出while循环,那么就可以说sleep()是不能被唤醒得,到时间后会自动进入就绪状态,等待cpu处理。同时sleep()中我们看到了synchronized,那也就意味着我们可以在任意地方调用该方法,这里我们要搞明白一个问题,那就是调用sleep()方法,到底是那个线程睡眠的问题,同样我们用代码去验证:

public class MyThread extends Thread {

        public MyThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            super.run();
            Log.e("tag", "------Before_sleep-------" + System.currentTimeMillis());

            try {
                sleep(6 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.e("tag", "------After_sleep-------" + System.currentTimeMillis());
        }
    }

 

首先我们在线程得run()方法中调用  sleep(6 * 1000),得到得结果是该线程睡眠,修改代码:

 public class MyThread extends Thread {

        public MyThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            super.run();
            Log.e("tag", "------Before_sleep_thread-------" + System.currentTimeMillis());
            MyThread1 myThread1 = new MyThread1("MyThread1");
            myThread1.start();

            Log.e("tag", "------After_sleep_thread-------" + System.currentTimeMillis());
        }
    }


    public class MyThread1 extends Thread {

        public MyThread1(String name) {
            super(name);
        }

        @Override
        public void run() {
            super.run();
            Log.e("tag", "------Before_sleep---111----" + System.currentTimeMillis());
            try {
                sleep(6 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.e("tag", "------After_sleep---111----" + System.currentTimeMillis());
        }
    }

安卓线程(基础概念与方法)_第1张图片

结果我想我们应该猜到了,正如我们想象的 MyThread 线程先打印Before_sleep_thread和After_sleep_thread,然后MyThread1 线程执行,打印Before_sleep---111, MyThread1 去睡眠,六秒后打印After_sleep---111,没有什么问题。那么我们再修改代码:


    public class MyThread extends Thread {

        public MyThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            super.run();
            Log.e("tag", "------Before_sleep_thread-------" + System.currentTimeMillis());
            MyThread1 myThread1 = new MyThread1("MyThread1");
            myThread1.start();
            try {
                myThread1.sleep(6000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Log.e("tag", "------After_sleep_thread-------" + System.currentTimeMillis());
        }
    }

    public class MyThread1 extends Thread {

        public MyThread1(String name) {
            super(name);
        }

        @Override
        public void run() {
            super.run();
            Log.e("tag", "------this is thread1---111----" + System.currentTimeMillis());
        }
    }

 

这次我们在 MyThread 里面执行 MyThread1 得 sleep() ,可能和我们想的不太一样,此时我们发现睡眠得不是MyThread1而是 MyThread,所以我们得到结论:调用某一个线程类的对象 t.sleep()睡眠的不是t,而是当前线程,那么说到这我们可能会想到一个问题,如果在主线程中调用某个线程类对象的 sleep() ,那睡眠的是谁呢?上面的结论是正确的,睡眠的是主线程,这个时候会想,主线程进行 Thread.Sleep()会导致ANR吗?以前我的理解是“主线程进行耗时操作”就会引起ANR,在这之后我觉得我们应该明白,ANR是 Application Not Responding,意思是”应用没有响应“我们注意没有响应,什么叫没有响应呢,可以理解为发出一个请求或者一种操作,但是没有得到反馈,这个叫没有响应。那么主线程如果sleep(50*1000)会出现ANR么?这个我们分析具体的情况,如果说在50s内,UI线程不再需要其他的任何操作,不会收到任何需要处理的消息,单纯的睡眠,那么就不会ANR,因为不需要 UI 线程给出响应;当然如果在这期间需要 UI 线程处理,比如更新 UI,而此时 UI 线程还在睡眠,那么就会ANR。

说完 sleep(),我们再来看wait():和 sleep()的区别是,wait()没有同步锁,所以再调用时要在同步代码块或者同步方法中,wait()是可以被唤醒的。

    public class MyThread extends Thread {

        public MyThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            super.run();
            Log.e("tag", "------Before_wait_thread-------" + System.currentTimeMillis());
            synchronized (shareObj) {
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                shareObj.notify();
            }

        }
    }

    public class MyThread1 extends Thread {

        public MyThread1(String name) {
            super(name);
        }

        @Override
        public void run() {
            super.run();
            synchronized (shareObj) {

                try {
                    shareObj.wait(50000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            Log.e("tag", "------this is thread1---111----" + System.currentTimeMillis());
        }
    }

这是一个简单的使用例子(看log的时间)

3、线程的还有一个interrupt()方法

这个方法既可以打断 sleep()也可以打断 wait()。

 

 

 

你可能感兴趣的:(线程与进程,线程的使用,主线程睡眠会ANR?,线程的生命周期)