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());
}
}
结果我想我们应该猜到了,正如我们想象的 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()。