Android的应用层都是Java写的,所以很多语法上的东西就是Java的,只不过某些类在原有基础上进行了包装,毕竟android跟java web,se之类的生产环境不一样,而线程又是java中一块很重要的知识,线程控制的好不好也关系到应用性能提升大不大,毕竟不是Java出身,所以简单的说说java的线程
多线程
扯到线程,就不免会说道进程,简答来数进程就是内存中运行的应用程序有独立的内存空间,而线程则是进程的组成,线程即达成目标的一个任务。
那么我们为什么使用多线程呢?如今cpu已经进入4核,八核时代,如何充分利用硬件资源去实现一个程序催生了多线程。
举个简单的例子,运一批货,在一个双车道的道路上,如果按顺序发车的话,货物到达肯定要慢点,同时发货的话,理想情况时间就少了一半。
Java的多线程
一般Java中实现多线程无非两种方式:
- 继承Thread
class NewThread extends Thread {
@Override
public void run() {
super.run();
}
}
- new Thread将任务放到Runnable里面
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
需要注意的是,调用start方法后,线程不是立即开启,而是等待系统分配时间片(由于系统是随机分配时间片,所以线程的调度存在不确定性)
线程状态
线程状态.png- 新建状态(New):**当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
- 就绪状态(Runnable):**当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
- 运行状态(Running):**当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
- 阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。 - 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程优先级
Java的线程是具有优先级的,可以通过setPriority()设置线程的优先级,控制线程的重要性(拥有较大机会获取系统调度),Thread有3个控制线程优先级的常量
- static int MAX_PRIORITY ----10
- static int MIN_PRIORITY-----1
- static int NORM_PRIORITY---5
常见线程常用函数
1.sleep(long mills):让当前线程休眠指定的毫秒数
2.join():让主线程等待调用该函数的线程结束再执行(所在线程等待调用该方法的线程变量所在的线程执行完毕)
应用情形:当主线程需要实时使用子线程的处理结果,有点像阻塞式的编程
3.yield():暂停当前线程,让出时间片
实质是将该线程调回到runnable状态重新等待调度,高优先级线程会被先调用,同等级无法保证让出时间片(进入了统一等待队列,系统还是会选中当前线程)
4.setPriority(int priority):上面有说过
5.interrupt():野蛮方式中断线程,可能会导致资源无法释放
6.wait():等待拥有同样资源锁的线程的执行,线程同步的一个方法,主要关系到对资源锁定问题,后面讨论
7.notify():唤醒拥有同样资源锁的线程
备注:sleep()与wait()区别:sleep的同时不释放同步锁,也就是说sleep的同时拥有同样资源锁的线程不会拿到时间片,而wait()的时候是释放同步锁,让其他拥有同等资源锁的线程获得时间片(必须放在synchronized块内)
线程同步问题
这个应该是线程里面最重要,也是最难的部分了,主要由于系统调度的不确定性引起,一般不做线程同步的话,出现了某些问题,调试都很难调试出来
数据的同步在数据库里面非常常见,设想线程A,B同时对一个变量进行++操作
int res = 0;
class A extends Thread {
@Override
public void run() {
super.run();
res=res+1;
System.out.print("A:res" + res);
}}
class B extends Thread {
@Override
public void run() {
super.run();
res=res+1;
System.out.print("B:res" + res); }}```
上述代码会有两种不同的输出
A:1
B:2
或者
B:1
A:2
这可能跟我预想的结果就不一样了,因此我们需要对资源进行同步
######synshronized关键字
这是用来加同步锁的关键字,他有两种作用域
1. 对象:某个对象内synchronized methodA(){},可以锁住这个对象的该方法(若该对象拥有多个synchronized方法,加锁后其它synchronized方法也无法调用),但不影响不同对象
2. 类:synchronized static methodA(){},对所有对象起效
除了方法前可以加synchronized,某个区块也可以加,他们的实质都是锁对象
```synchronized methodA(){} //锁对象
synchronized static methodA(){} //锁类```
等同于
```methodA(){synchronized(obj){} } //锁对象
methodA(){
synchronized(obj.class){}}```
synchronized搭配wait(),notify()方法使用,wait()调用后,不执行wait()后面的代码,notify()后继续执行上次wait()后的代码
######lock同步锁
方便的解决同步问题的另一方案
class X {
// 显示定义Lock同步锁对象,此对象与共享资源具有一对一关系
private final Lock lock = new ReentrantLock();
public void m(){
// 加锁 lock.lock();
//... 需要进行线程安全同步的代码
// 释放Lock锁
lock.unlock(); } }
可以参考下Condition实现
######线程数据传递
由于线程的不确定性,所以线程间的通信,也不像普通的同步模式下的通信
1. 构造方法传递数据 由于构造方法是线程一开始就传入所以不存在同步问题,可通过重载构造方法传入参数
2. 通过公开方法赋值,类的属性器赋值或者读取
前两个都建立在宿主线程与被调线程的通信,下面就是线程与线程之间(并无直接关联)
3. 通过同步控制,访问共有对象(synchronized,wait,notify等)
4. 管道流(这个不是很了解,望高手指教)
---
接下来就谈谈Runnable,Callable<V>,Future。
由于Runnable里面的run函数返回为void,因此我们无法观察任务的状态,Callable<V>正好解决了这个问题
public interface Callable<V> {
/**
- Computes a result, or throws an exception if unable to do so.
- @return computed result
- @throws Exception if unable to compute a result
*/
V call() throw Exception;
}```
Future又是什么东西呢,
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException,ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}```
* cancel取消任务
* isCancelled 返回任务是否被取消
* get 获取任务这行结果,阻塞方法,等到任务正常完成才会返回值
* get(long,TimeUnit)在规定时间内无返回结果就返回null
所以Future只是个接口因此我们只能使用它的实现
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}```
FutrueTask实现了RunnableFuture<v>,RunnableFuture<v>又继承自Runnable,Future<v>,Callable<v>经常搭配ExcutorService一起使用
public FutureTask(Callable<V> callable) {}
public FutureTask(Runnable runnable, V result) {}```
FutureTask提供了两个构造函数,实质都是实现Runnable与Future<v>接口,[FutureTask,Callable<V>使用](http://www.cnblogs.com/dolphin0520/p/3949310.html)
----
写作思路大抵发散性的 有相关的就查阅下,篇幅比较长,若有错误请大家指正,下次接着写线程池
网友评论