多线程实现之Thread和Runnable
Android的多线程实现最基础部分应该该为Thread和Runnable,通常情况下使用两种方式的启动方式如下:
private void startNewThread(){
new Thread(){
@Override
public void run(){
//耗时操作
}
}.start();
}
启动带Runnable参数
private void startNewThread(){
new Thread(new Runnable(){
@Override
public void run(){
//耗时操作
}
}).start();
}
对示例一,实例二代码进行分析:实例一中代码使用Thread内的run函数执行操作,实例二中代码实际上为Thread的构造函数传递了一个Runnable对象,使用Runnable对象内的run方法执行耗时操作。两者之间的区别分析如下:
Thread源码如下:
class Thread implements Runnable {
//线程所属的ThreadGroup
private ThreadGroup group;
//需要执行的Runnable对象
private Runnable target;
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
Thread parent = currentThread();
if (g == null) {
g = parent.getThreadGroup();
}
g.addUnstarted();
this.group = g;
this.target = target;
this.priority = parent.getPriority();
this.daemon = parent.isDaemon();
setName(name);
//代码省略
}
//为了节约长度,只提取了核心代码进行处理
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
nativeCreate(this, stackSize, daemon);
started = true;
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
从官方源代码中对init函数功能描述为初始化线程,第一个参数ThreadGroup为线程组,第二个参数target为当前线程的Runnable对象。源码init函数功能注释为:
group threads created/set up by the VM. Any new functionality added to this method in the future may have to also be added to the VM.
中文对应翻译过来即为:线程组在Android的虚拟机中创建或者设置,任何新功能都能被添加到该方法中在以后将会被添加到Android的虚拟机中。
对上述代码中start函数进行分析,根据group.add(this)函数即为将当前线程添加到线程组中,依据Android虚拟机自行调度运行中。对源代码的run方法进行分析,当启动一个线程时,如果Thread的target不为空,则会在子线程汇总执行这个target的run方法。否则虚拟机将会执行线程自身的run函数。
线程的wait,sleep,join和yield方法
函数名 | 含义 |
---|---|
wait | 线程执行后,进入到与对象相关的等待池中,同时释放了对象的机锁。使其他线 程可以访问,用户可以使用notify、notifyAll或者指定睡眠时间后唤醒 |
sleep | 该函数为Thread的静态函数,使调用线程进入睡眠状态,由于是static方法, 所以无法改变对象的机锁。所以,当在一个synchronized块中调用sleep方法。虽然休眠了,但是对象的机制并没有释放,其他线程无法访问该对象 |
join | 等待目标线程执行完成之后再继续执行 |
yield | 线程礼让,目标线程由运行状态换为就绪状态,即让出执行权限,其他线程优先执行。其他线程是否能够优先执行不可知。 |
Notes:为提高程序的可理解性,此处以及下面引入synchronized机制
以下为对wait,notify与notifyAll的应用
public static void main(String[] args) {
System.out.println("主线程运行");
Thread thread=new WaitThread();
thread.start();
long starttime=System.currentTimeMillis();
try {
synchronized (sLockObject) {
System.out.println("主线程等待");
sLockObject.wait();
}
} catch (Exception e) {
// TODO: handle exception
}
long timeMs=(System.currentTimeMillis()-starttime);
System.out.println("主线程继续-->等待耗时:"+timeMs+" ms");
}
static class WaitThread extends Thread{
public void run() {
// TODO Auto-generated method stub
try {
synchronized (sLockObject) {
Thread.sleep(3000);
sLockObject.notifyAll();
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
当前运行结果为:
>
主线程运行
主线程等待
主线程继续–>等待耗时:3001 ms
上述代码为,首先在主线程中synchronized块中调用wait方法,使WaitThread进入等待池,此时主线程停止运行,由于WaitThread中的synchronized块为设置睡眠时间为3秒,三秒后调用notifyAll()方法,使WaitThread线程正常运行,主线程也依次运行
以下为对join函数的调用
public static void main(String[] args) {
Worker worker1=new Worker("work-1");
Worker worker2=new Worker("work-2");
worker1.start();
System.out.println("启动线程1");
try {
worker1.join();
System.out.println("启动线程2");
worker2.start();
worker2.join();
} catch (Exception e) {
// TODO: handle exception
}
}
static class Worker extends Thread{
public Worker(String name){
super(name);
}
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(2000);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("work in "+getName());
}
}
执行结果如下:
>
启动线程-1
work in work-1
启动线程-2
work in work-2
由于使用join机制,使得线程不得不挨个运行,主线程内先执行worker1.start()方法,此次work1执行,随后立即调用work1的join方法,导致主线程拥塞,优先执行work1线程,依次类推work2线程处理方式也一样。为了方便理解,对调用join函数部分使用注释,以下为修改后的结果:
>
启动线程1
启动线程2
work in work-2
work in work-1
Notes:此处work1和work2执行结束顺序不分先后
以下为对join函数的调用
static class YieldThread extends Thread{
public YieldThread(String name){
super(name);
}
public synchronized void run(){
for(int i=0;i<5;i++){
System.out.println(this.getName()+" "+this.getPriority()+"--->"+i);
if(i==2){
Thread.yield();
}
}
}
}
public static void main(String[] args) {
YieldThread t1=new YieldThread("thread-1");
YieldThread t2=new YieldThread("thread-2");
t1.start();
t2.start();
}
执行结果如下:
>
thread-1 5—>0
thread-1 5—>1
thread-1 5—>2
thread-2 5—>0
thread-2 5—>1
thread-2 5—>2
thread-1 5—>3
thread-1 5—>4
thread-2 5—>3
thread-2 5—>4
Notes:由于t1,t2运行先后顺序不确定,导致运行结果不唯一,截取显示的为最易功能分析的结果
对上述代码分析可以得出,run函数内部对i的情形进行判断,如果i为2时,则当前线程让出执行,让另一线程优先执行。即无论t1,t2谁的i值先到到2都会让出当前线程,让另一线程执行,当让出线程执行后i值也等于2时,则次线程也让出。随后则依次执行完各自线程的内容,即程序结束