您的位置:首页 > 移动开发 > Android开发

Android和Java各种线程的区别和认识

2015-03-09 22:38 429 查看
线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

那么线程有哪几种呢? 我整理出来有关于Java和Android线程的大概是这些:

守护线程(DaemonThread)

用户线程(UserThread)

Handler

HandlerThread

AsyncTask

Java有两种Thread:“守护线程Daemon”与“用户线程User”。

守护线程(DaemonThread)

守护线程具有最低的优先级,用于为系统中的其它对象和线程提供服务。将一个用户线程设置为守护线程的方式是在线程对象调用start()方法之前调用线程对象的setDaemon方法。

典型的守护线程例子是JVM中的系统资源自动回收线程,我们所熟悉的Java垃圾回收线程就是一个典型的守护线程,当我们的程序中不再有任何运行中的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是Java虚拟机上仅剩的线程时,Java虚拟机会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。

守护线程使用的情况较少,但并非无用,JVM的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等。

守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。也就是说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”。那Java的守护线程是什么样子的呢。当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则JVM不会退出。

守护线程是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去。

将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现。在使用守护线程时需要注意一下几点:

(1)thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。

(2)在Daemon线程中产生的新线程也是Daemon的。

(3)守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。


用户线程(UserThread)

用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。

在Java中,“线程”指两件不同的事情:

java.lang.Thread类的一个实例。

如果是扩展java.lang.Thread类的线程,则直接new即可。

class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
//run方法需要重载
public void run() {
// compute primes larger than minPrime
. . .
}
}


如果是实现了java.lang.Runnable接口的类,则必须实现Runnable的run()方法。

public interface Runnable
{
public voic run();//线程要执行的方法
}


Thread与Runnable的区别在于,Thread无法资源共享,而Runnable可以资源共享

class MyThread implements Runnable{

private int ticket = 5;  //5张票

public void run() {

for (int i=0; i<=20; i++) {

if (this.ticket > 0) {                      System.out.println(Thread.currentThread().getName()+ "正在卖票"+this.ticket--);

}
}
}
}

public class lzwCode {

public static void main(String [] args) {

MyThread my = new MyThread();

new Thread(my, "1号窗口").start();
new Thread(my, "2号窗口").start();
new Thread(my, "3号窗口").start();

}

}


打印结果




线程的执行。

在线程的Thread对象上调用start()方法,而不是run()或者别的方法。

说到线程的执行就要提到线程的几种状态:

Java中的线程的生命周期大体可分为5种状态。

NEW:这种情况指的是,通过New关键字创建了Thread类(或其子类)的对象

RUNNABLE:这种情况指的是Thread类的对象调用了start()方法,这时的线程就等待时间片轮转到自己这,以便获得CPU;第二种情况是线程在处于RUNNABLE状态时并没有运行完自己的run方法,时间片用完之后回到RUNNABLE状态;还有种情况就是处于BLOCKED状态的线程结束了当前的BLOCKED状态之后重新回到RUNNABLE状态。

RUNNING:这时的线程指的是获得CPU的RUNNABLE线程,RUNNING状态是所有线程都希望获得的状态。

DEAD:处于RUNNING状态的线程,在执行完run方法之后,就变成了DEAD状态了。

BLOCKED:这种状态指的是处于RUNNING状态的线程,出于某种原因,比如调用了sleep方法、等待用户输入等而让出当前的CPU给其他的线程。

处于RUNNABLE状态的线程变为BLOCKED状态的原因,除了该线程调用了sleep方法、等待输入原因外,还有就是在当前线程中调用了其他线程的join方法、当访问一个对象的方法时,该方法被锁定等。

相应的,当处于BLocked状态的线程在满足以下条件时就会由该状态转到RUNNABLE状态,这些条件是:sleep的线程醒来(sleep的时间到了)、获得了用户的输入、调用了join的其他线程结束、获得了对象锁。

一般情况下,都是处于RUNNABLE的线程和处于RUNNING状态的线程,互相切换,直到运行完run方法,线程结束,进入DEAD状态。

Android对于线程有一些比较特殊的地方,首先在android中,只有主线程也就是UI线程才有权利去更改Activity界面上的组件,而如果应用程序所做的事情在主线程里占用了太长的时间的话,就会引发ANR对话框,因为你的应用程序并没有给自己机会来处理输入事件或者Intent广播。

因此,运行在主线程里的任何方法都尽可能少做事情。如果要做一些长时间的事情,则需要开启一些子线程来单独运行

Handler

android.os.Handler是Android SDK中处理定时操作的核心类。通过Handler类,可以提交和处理一个Runnable对象。这个对象的run 方法可以立刻执行,也可以在指定时间之后执行(可以称为预约执行)。

handler类允许你发送消息和处理线程消息队列中的消息及runnable对象。handler实例都是与一个线程和该线程的消息队列一起使用,一旦创建了一个新的handler实例,系统就把该实例与一个线程和该线程的消息队列捆绑起来,这将可以发送消息和runnable对象给该消息队列,并在消息队列出口处处理它们。


在Handler 异步实现时,涉及到 Handler, Looper, Message,Thread四个对象,实现异步的流程是主线程启动Thread(子线程)àthread(子线程)运行并生成Message-àLooper获取Message并传递给HandleràHandler逐个获取Looper中的Message,并进行UI变更。

使用的优点:

结构清晰,功能定义明确

对于多个后台任务时,简单,清晰

使用的缺点:

在单个后台异步处理时,显得代码过多,结构过于复杂(相对性)

handler类有两种主要用途:

按照时间计划,在未来某时刻,对处理一个消息或执行某个runnable实例。

把一个对另外线程对象的操作请求放入消息队列中,从而避免线程间冲突。

时间类消息通过如下方法使用:

post(Runnable)

postAtTime(Runnable, long)

postDelayed(Runnable, long)

sendEmptyMessage(int)

sendMessage(Message)

sendMessageAtTime(Message, long)

sendMessageDelayed(Message, long)

handleMessage(Message msg)

removeMessages(int what)

post之类函数可以传输一个runnable对象给消息队列,并在到达消息队列后被调用。sendmessage之类函数可以传送一个包含数据的message对象,该message对象可以被Handler类的handleMessage(Message) 方法所处理。
post之类函数和sendmessage之类的函数都可以指定消息的执行时机,是立即执行、稍后一段时间执行,还是在某个确定时刻执行。这可以用来实现超时、消息或其他时间相关的操作。
当一个进程启动时,主线程独立执行一个消息队列,该队列管理着应用顶层的对象(如:activities、broadcast receivers等等)和所有创建的窗口。你可以创建自己的一个线程,并通过handler来与主线程进行通信。这可以通过在新的线程中调用主线程的handler的post和sendmessage操作来实现。
Handler类主要可以使用如下3个方法来设置执行Runnable对象的时间:


01.//立即执行Runnable对象
02.public final boolean post(Runnable r);
03.//在指定时间uptimeMillis 后执行Runnable对象
04.public final boolean postAtTime(Runnable r,long uptimeMillis);
05.//在指定的时间间隔delayMillis 执行Runnable对象
06.public final boolean postDelayed(Runnable r,long delayMillis);


要注意的是,不管使用哪个方法来执行Runnable对,都只能运行一次。如果想循环执行,必须在执行完后再次调用post、postAtTime或postDelayed方法。

例如,在Run方法中再次调用postDelayed方法,代码如下:

01.public void run()
02.{
03.    tvCount.setText("Count:" + String.valueOf(++count));
04.    //现次调用postDelayed方法,5秒后run方法仍被调用,然后再一次同用postDelayed方法,这样就行成了循环调用
05.    handler.postDelayed(this, 5000);
06.}


且handler.post(r)其实这样并不会新起线程,只是执行的runnable里的run()方法,却没有执行start()方法,所以runnable走的还是UI线程。

而send方法之类的函数则是用来发送某些消息

01.//发送指定的消息,该方法为仅仅只是传递一个int值来表示发送的消息类型。
02.public final void sendEmptyMessage(int what);
03.//通过Message发送消息(这样可以将数据包装进去,一起发送过去)
04.public final void sendMessage(Message msg);
05.//即在确定的时间发送这个消息,这个时间通过这个参数指定这个时间由uptimeMillis()传递
06.public final boolean sendMessageAtTime(Message msg, long uptimeMillis);
07.//在延迟特定的时间后发送这个消息
08.public final boolean sendMessageDelayed(Message msg, long timeMillis);


在发送消息后,则需要一个处理消息的地方,即handleMessage方法,该方法是需要重写的方法,该方法为处理消息的核心函数。

1 Handler handler = new Handler(){
2           @Override
3           public void handleMessage(Message msg){
4                    //获得通过handler.sendEmptyMessage发送的消息编码
5                    int what = msg.what;
6                    /* 处理代码 */
7                    //一般使用switch方法来判断处理what
8           }
9 }


取消消息的发送使用removeMessages(int what)。

HandlerThread

Handler会关联一个单独的线程和消息队列。Handler默认关联主线程,虽然要提供Runnable参数 ,但默认是直接调用Runnable中的run()方法。也就是默认下会在主线程执行,如果在这里面的操作会有阻塞,界面也会卡住。如果要在其他线程执行,可以使用HandlerThread。

HandlerThread的使用

HandlerThread uIhandlerThread = new HandlerThread("update");
uIhandlerThread.start();
//Handler UIhandler = new Handler(uIhandlerThread.getLooper());
Handler uIhandler = new Handler(uIhandlerThread.getLooper(),new Callback() {
public boolean handleMessage(Message msg) {
Bundle b = msg.getData();
int age = b.getInt("age");
String name = b.getString("name");
System.out.println("age is " + age + ", name is" + name);
System.out.println("Handler--->" + Thread.currentThread().getId());
System.out.println("handlerMessage");
return true;
}
});


当要停止uIhandlerThread执行时用:

if(uIhandlerThread!=null) {
pointThread.quit();
}


AsyncTask

AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程.

使用的优点:

简单,快捷

过程可控

使用的缺点:

在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来。

首先明确Android之所以有Handler和AsyncTask,都是为了不阻塞主线程(UI线程),且UI的更新只能在主线程中完成,因此异步处理是不可避免的。

Android为了降低这个开发难度,提供了AsyncTask。AsyncTask就是一个封装过的后台任务类,顾名思义就是异步任务。

AsyncTask直接继承于Object类,位置为android.os.AsyncTask。要使用AsyncTask工作我们要提供三个泛型参数,并重载几个方法(至少重载一个)。

AsyncTask定义了三种泛型类型 Params,Progress和Result。

Params 启动任务执行的输入参数,比如HTTP请求的URL。

Progress 后台任务执行的百分比。

Result 后台执行任务最终返回的结果,比如String。

并且一个异步加载数据最少要重写以下这两个方法:

doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。

onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回

有必要的话你还得重写以下这三个方法,但不是必须的:

onProgressUpdate(Progress…) 可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。

onPreExecute() 这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。

onCancelled() 用户调用取消时,要做的操作

使用AsyncTask类,以下是几条必须遵守的准则:

Task的实例必须在UI thread中创建;

execute方法必须在UI thread中调用;

不要手动的调用onPreExecute()、onPostExecute(Result)、doInBackground(Params…)、 onProgressUpdate(Progress…)这几个方法;

该task只能被执行一次,否则多次调用时将会出现异常;

一个超简单的理解 AsyncTask 的例子:

main.xml

1.<?xml version="1.0" encoding="utf-8"?>
2.<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3.    android:orientation="vertical"
4.    android:layout_width="fill_parent"
5.    android:layout_height="fill_parent"
6.    >
7.    <TextView
8.    android:id="@+id/textView01"
9.    android:layout_width="fill_parent"
10.    android:layout_height="wrap_content"
11.    />
12.   <ProgressBar
13.   android:id="@+id/progressBar02"
14.    android:layout_width="fill_parent"
15.    android:layout_height="wrap_content"
16.    style="?android:attr/progressBarStyleHorizontal"
17.    />
18.    <Button
19.    android:id="@+id/button03"
20.    android:layout_width="fill_parent"
21.    android:layout_height="wrap_content"
22.    android:text="更新progressbar"
23.    />
24.</LinearLayout>


MainActivity.java

1.package vic.wong.main;
2.
3.import android.app.Activity;
4.import android.os.Bundle;
5.import android.view.View;
6.import android.view.View.OnClickListener;
7.import android.widget.Button;
8.import android.widget.ProgressBar;
9.import android.widget.TextView;
10.
11.public class MainActivity extends Activity {
12.    private Button button;
13.    private ProgressBar progressBar;
14.    private TextView textView;
15.
16.    @Override
17.    public void onCreate(Bundle savedInstanceState) {
18.        super.onCreate(savedInstanceState);
19.        setContentView(R.layout.main);
20.
21.        button = (Button)findViewById(R.id.button03);
22.        progressBar = (ProgressBar)findViewById(R.id.progressBar02);
23.        textView = (TextView)findViewById(R.id.textView01);
24.
25.        button.setOnClickListener(new OnClickListener() {
26.
27.            @Override
28.            public void onClick(View v) {
29.                ProgressBarAsyncTask asyncTask = new ProgressBarAsyncTask(textView, progressBar);
30.                asyncTask.execute(1000);
31.            }
32.        });
33.    }
34.}


NetOperator.java

1.package vic.wong.main;
2.
3.
4.//模拟网络环境
5.public class NetOperator {
6.
7.    public void operator(){
8.        try {
9.            //休眠1秒
10.            Thread.sleep(1000);
11.        } catch (InterruptedException e) {
12.            // TODO Auto-generated catch block
13.            e.printStackTrace();
14.        }
15.    }
16.
17.}


ProgressBarAsyncTask .java

1.package vic.wong.main;
2.import android.os.AsyncTask;
3.import android.widget.ProgressBar;
4.import android.widget.TextView;
5.
6./**
7. * 生成该类的对象,并调用execute方法之后
8. * 首先执行的是onProExecute方法
9. * 其次执行doInBackgroup方法
10. *
11. */
12.public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {
13.
14.    private TextView textView;
15.    private ProgressBar progressBar;
16.
17.
18.    public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {
19.        super();
20.        this.textView = textView;
21.        this.progressBar = progressBar;
22.    }
23.
24.
25.    /**
26.     * 这里的Integer参数对应AsyncTask中的第一个参数
27.     * 这里的String返回值对应AsyncTask的第三个参数
28.     * 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改
29.     * 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作
30.     */
31.    @Override
32.    protected String doInBackground(Integer... params) {
33.        NetOperator netOperator = new NetOperator();
34.        int i = 0;
35.        for (i = 10; i <= 100; i+=10) {
36.            netOperator.operator();
37.            publishProgress(i);
38.        }
39.        return i + params[0].intValue() + "";
40.    }
41.
42.
43.    /**
44.     * 这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值)
45.     * 在doInBackground方法执行结束之后在运行,并且运行在UI线程当中 可以对UI空间进行设置
46.     */
47.    @Override
48.    protected void onPostExecute(String result) {
49.        textView.setText("异步操作执行结束" + result);
50.    }
51.
52.
53.    //该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置
54.    @Override
55.    protected void onPreExecute() {
56.        textView.setText("开始执行异步线程");
57.    }
58.
59.
60.    /**
61.     * 这里的Intege参数对应AsyncTask中的第二个参数
62.     * 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行
63.     * onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作
64.     */
65.    @Override
66.    protected void onProgressUpdate(Integer... values) {
67.        int vlaue = values[0];
68.        progressBar.setProgress(vlaue);
69.    }
70.
71.  }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: