Android多线程-Thread的理解和使用

本文探讨了Android中的Thread类,详细介绍了Thread的主要函数,并重点讨论了线程使用中如何终止线程以及线程安全和线程同步的概念。线程安全通过同步代码块或者使用ReentrantLock进行实现,lock()用于获取锁,unlock()用于释放锁。

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

一、Thread的定义

线程,可以看作是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。

1.1 Thread主要函数

1.run(): 线程运行时所执行的代码
2.start():启动线程
3.sleep()/sleep(long millis):线程休眠,进入阻塞状态,sleep方法不会释放锁
(其它线程不会进入synchronized方法体或方法块,不释放锁需要try/catch)
4.yield():线程交出CPU,但是不会阻塞而是重置为就绪状态,不会释放锁
5.join()/join(long millis)/join(long millis,int nanoseconds):线程插队,当该子线程执行完毕后接着执行其它
6.wait():进入阻塞状态,释放锁(其它线程可以进入synchronized方法体或方法块,释放锁不需要try/catch)
7.interrupt():中断线程,注意只能中断阻塞状态的线程
8.getId():获取当前线程的id
9.getName()/setName():获取和设置线程的name
10.getPriority()/setPriority():获取和设置线程的优先级,范围1-10,默认是5
11.setDaemon()/isDaemo():设置和获取是否守护线程
12.currentThread():静态函数获取当前线程

1.2 Thread的几种状态

新建状态(new):实例化之后进入该状态;
就绪状态(Runnable):线程调用start()之后就绪状态等待cpu执行,注意这时只是表示可以运行并不代表已经运行;
运行状态(Running):线程获得cpu的执行,开始执行run()方法的代码;
阻塞状态(Blocked):线程由于各种原因进入阻塞状态:join()、sleep()、wait()、等待触发条件、等待由别的线程占用的锁;
死亡状态(Dead):线程运行完毕或异常退出,可使用isAlive()获取状态。

二、Thread的使用

1. 继承Thread,重写run()方法。
public class MyThread extends Thread{

        @Override
        public void run() {
            super.run();
            // do something
        }
    }
    // 启动Thread
    public void goThread(){
        new MyThread().start();
    }
2.   实现Runnable,重写run()方法来执行任务。
public class MyRunnable implements Runnable{

        @Override
        public void run() {
            // do something
        }
    }
   //启动
    new Thread(new MyRunnable()).start();
new Thread(new Runnable() {
        @Override
        public void run() {
            
        }
    }).start();

3.   通过Handler启动线程。

private int count = 0;
    private Handler mHandler = new Handler();

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            Log.i("download",Thread.currentThread().getName()+":"+count);
            count ++;
            mHandler.postDelayed(runnable,1000); // 执行后延迟1000毫秒再次执行,count记录执行次数
        }
    };

三、Thread使用其它问题

3.1 如何终止线程

1.   使用boolean变量作为标记
public class ThreadStopTest extends Thread {
//关键字volatile表示这个变量随时都有可能发生变化,主要是表示同步,也就是同一时刻只能由一个线程来修改该变量的值。
    public volatile boolean stop = false;
    @Override
    public void run() {
        super.run();
        while (!stop){ //如果变量stop为true则跳出run()方法
            // thread runing
        }
    }
    
}
2.   使用interrupt()
该方法可以用来中断进程,然后就可以终止线程,使用该方法分两种情况,线程 正常运行状态 阻塞状态
(1) 线程正常运行状态
// 定义开始和结束线程的方法,与按钮绑定
    public void goThread(){
        if(null == myThread){
            myThread = new MyThread();
        }
        myThread.start();
    }
    private void stopThread() {
        if(null != myThread && myThread.isAlive()){
            myThread.interrupt(); //interrupt()方法只是中断线程而不是结束线程
            myThread = null;
        }
    }//在线程正常运行状态下使用该方法是不能结束线程的

    public class MyThread extends Thread{
        @Override
        public void run() {
            super.run();
            int i = 0;
            // 判断状态,如果被打断则跳出并将线程置空
            while (!isInterrupted()){
                i++;
                Log.i("thread",Thread.currentThread().getName()+":Running()_Count:"+i);
            }
        }
    }

(2) 线程阻塞状态

public class SleepThread extends Thread{
        @Override
        public void run() {
            super.run();
            int i = 0;
            while(true){
                try {
                    i++;
                    Thread.sleep(1000);
                    Log.i("thread",Thread.currentThread().getName()+":Running()_Count:"+i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Log.i("thread",Thread.currentThread().getName()+"异常抛出,停止线程");
                    break;
                }
            }

        }
    }
调用interrupt()方法后抛出InterruptedException异常,同时break跳出循环达到停止跑代码的作用。
原理是当线程进入阻塞状态时,调用interrupt()方法会抛出异常,利用这个异常来跳出循环。

(3) 两种状态一起处理
此外,把两种状态的处理结合在一起是比较好的,这样既可以及时判断线程状态又可以捕获异常来跳出循环。

public class SleepThread extends Thread{
        @Override
        public void run() {
            super.run();
            int i = 0;
            while(!isInterrupted()){  // 判断线程是否被打断
                try {
                    i++;
                    Thread.sleep(1000);
                    Log.i("thread",Thread.currentThread().getName()+":Running()_Count:"+i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Log.i("thread",Thread.currentThread().getName()+"异常抛出,停止线程");
                    break;// 抛出异常跳出循环
                }
            }

        }
    }
3.   使用stop()方法终止线程
这个相当于强制关机,可能会丢失数据 不推荐

3.2 线程安全与线程同步

1.   什么是线程安全问题
简单地说,线程安全问题是指多个线程访问同一代码或数据,造成结果和数据的错乱或与期望的结果不同所产生的问题。
2.   如何解决线程安全问题
基本上所有的并发模式在解决线程安全问题的问题上,都采用“序列化访问临界资源”的方案,即在同一时刻只能有
一个线程访问临界资源(多个线程可能同时访问的数据或资源),也称同步互斥访问。
(1) synchronized 关键字,保证同时刻只有一个线程进入该方法或者代码块,使用方式(Tips:java中有很多方式来实
现线程同步,我们常用的synchronized是效率最低的但是方便):
  • 线程run()方法中要执行的代码方法添加synchronized关键字,注意要添加在方法的返回值前。
int count = 100;
   private synchronized void count() {
        if (count > 0) {
            Log.e(TAG, Thread.currentThread().getName() + "--->" + count--);
        } else {
            isRunning = false;
        }
    }
  • 同步代码块的形式使用。
private void count() {
        synchronized (this) {
            if (count > 0) {
                Log.e(TAG, Thread.currentThread().getName() + "--->" + count--);
            } else {
                isRunning = false;
            }
        }
    }
(2) 特殊域变量 volatile 修饰变量:告诉虚拟机该变量随时可能更新,因此使用时每次都会重新计算,而不是使用寄存器的值。volatile不会提供任何原子操作,它也不能用来修饰final类型的变量。(不能完全保证线程安全)

private volatile int count = 1000;

(3)使用重入锁实现线程同步。
  • ReentrantLock() : 创建一个ReentrantLock实例
  • lock() :获得锁
  • unlock() : 释放锁
private void count() {
        lock.lock();
        if (count > 0) {
            Log.e(TAG, Thread.currentThread().getName() + "--->" + count--);
        } else {
            isRunning = false;
        }
        lock.unlock();
    }
(4)ThreadLocal管理变量。如果一个变量使用ThreadLocal进行管理,每一个使用该变量的线程都会获得该变量的副本,副本之间相互独立,所以每个线程都可以修改变量而不会对其它线程造成影响。
private static ThreadLocal<Integer> number = new ThreadLocal<Integer>(){
        // 重写方法,设置默认值
        @Override
        protected Integer initialValue() {
            return 1;
        }
        // 自定义方法设置变量值
        public void saveNumber(int newNumber){
            number.set(number.get() + newNumber);
        }
        // 自定义方法获取变量值
        public int getNumber(){
            return number.get();
        }
    };




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值