背景
多线程:顾名思义,它就是指的多个线程同时存在,在Android开发中我们经常需要在一些子线程(非UI线程)中去处理一些耗时操作。对于线程还不太了解的小伙伴可以看一下你真的了解线程吗。OK,那么多线程到底有什么优缺点呢?
优点:
1)适当的提高程序的执行效率(多个线程同时执行)。
2)适当的提高了资源利用率(CPU、内存等)。
缺点:
1)占用一定的内存空间。
2)线程越多CPU的调度开销越大。
3)程序的复杂度会上升。
线程安全
synchronized
同步块大家都比较熟悉,通过 synchronized 关键字来实现;所有加上 synchronized 的方法和块语句,在多线程访问的时候,同一时刻只能有一个线程能够访问。对于synchronized的使用有很多技巧和细节,用在不同的位置,锁不一样的Object效果都不一样,大家可以自行上网查一下
wait()和sleep()的区别
sleep():在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),该线程不丢失任何监视器的所属权,sleep() 是 Thread 类专属的静态方法,针对一个特定的线程。
wait() 方法使实体所处线程暂停执行,从而使对象进入等待状态,直到被 notify() 方法通知或者 wait() 的等待的时间到。sleep() 方法使持有的线程暂停运行,从而使线程进入休眠状态,直到用 interrupt 方法来打断他的休眠或者 sleep 的休眠的时间到。
wait() 方法进入等待状态时会释放同步锁,而 sleep() 方法不会释放同步锁。所以,当一个线程无限 sleep 时又没有任何人去 interrupt 它的时候,程序就产生大麻烦了,notify() 是用来通知线程,但在 notify() 之前线程是需要获得 lock 的。另个意思就是必须写在 synchronized(lockobj) {…} 之中。wait() 也是这个样子,一个线程需要释放某个 lock,也是在其获得 lock 情况下才能够释放,所以 wait() 也需要放在 synchronized(lockobj) {…} 之中。
volatile 关键字
volatile 是一个特殊的修饰符,只有成员变量才能使用它。在Java并发程序缺少同步类的情况下,多线程对成员变量的操作对其它线程是透明的。volatile 变量可以保证下一个读取操作会在前一个写操作之后发生。线程都会直接从内存中读取该变量并且不缓存它。这就确保了线程读取到的变量是同内存中是一致的。
join() 方法
join() 方法定义在 Thread 类中,所以调用者必须是一个线程,join() 方法主要是让调用该方法的 Thread 完成 run() 方法里面的东西后,再执行 join() 方法后面的代码,看下下面的”意思”代码:
Thread t1 = new Thread(计数线程一);
Thread t2 = new Thread(计数线程二);
t1.start();
t1.join(); // 等待计数线程一执行完成,再执行计数线程二
t2.start();
启动 t1 后,调用了 join() 方法,直到 t1 的计数任务结束,才轮到 t2 启动,然后 t2 才开始计数任务,两个线程是按着严格的顺序来执行的。如果 t2 的执行需要依赖于 t1 中的完整数据的时候,这种方法就可以很好的确保两个线程的同步性。
yield()和sleep()的区别
Thread.sleep(long time):线程暂时终止执行(睡眠)一定的时间。
Thread.yield():线程放弃运行,将CPU的控制权让出。
这两个方法都会将当前运行线程的CPU控制权让出来,但 sleep() 方法在指定的睡眠时间内一定不会再得到运行机会,直到它的睡眠时间完成;而 yield() 方法让出控制权后,还有可能马上被系统的调度机制选中来运行,比如,执行yield()方法的线程优先级高于其他的线程,那么这个线程即使执行了 yield() 方法也可能不能起到让出CPU控制权的效果,因为它让出控制权后,进入排队队列,调度机制将从等待运行的线程队列中选出一个等级最高的线程来运行,那么它又(很可能)被选中来运行。
Android中的多线程和主线问题
在Android中我们实现子线程主要有两种形式:
- AsynTask是一个抽象的泛型类它提供了Params,Progress,Result三个泛型参数。
public abstract class AsyncTask<Params, Progress, Result>
Params:表示AsynTask执行异步任务的时候的参数类型,比如下载文件的时候的Url
Progress:后台执行任务的进度类型
Result:后台执行任务完成返回的结果类型
AsynTask一般需要重写四个方法
onPreExecute():在执行异步任务的之前会调用,用于前期准备工作。
doInBackground(Params…params):用于执行异步任务
onProgressUpdate(Progress…progress):当异步任务的进度发生变化的时候调用。注意:这个方法不会自动执行,需要手动在
doInBackground方法里面通过调用publishProgress方法,publishProgress才会调用onProgressUpdate方法更新进度,该方法是在主线程中执行。
onPostExecute(Result result) :在异步任务执行完成之后,该方法就会被调用,result值就是doInBackground返回值。
这个四个方法执行执行次序就是:
onPreExecute->doInBackground->onProgressUpdate->onProgressUpdate - Thread+Handler的形式
主要就是我们在子线程中完成耗时操作之后通过Handler来重新回到主线程来进行一些UI操作。
总结
以上就是对于JAVA中多线程的介绍和Android中线程的使用,欢迎大家指出不足,相互交流,后面会在写一篇关于线程池的文章一大家一起学习。