Android之多线程解析(一)之Thread、Runnable

本文介绍了Android中多线程的实现方式,包括Thread和Runnable的使用。详细分析了Thread的构造函数和start方法,以及线程的wait、sleep、join和yield方法的用法。通过示例代码展示了wait、notify与notifyAll的配合使用,以及join函数如何控制线程的执行顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多线程实现之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时,则次线程也让出。随后则依次执行完各自线程的内容,即程序结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值