展开说说:Android实现多线程几种方式

1、线程是什么

关于线程,Android开发经常遇到的一个和线程相关的异常报错:NetworkOnMainThreadException,因为网络请求不可以运行在主线程(又称UI线程)。和网络请求一样的还有I/O操作、数据库操作等耗时任务一样都只能在子线程运行,他们都被成为耗时操作。那么线程到底是什么?线程是操作系统进行调度的最小单元,我们写的每一行代码都是在线程内执行的。所有更新UI的操作包括view、dialog、toast等展示和更新都必须在主线程执行,但如果在主线程执行耗时操作就会导致其他UI更新无法及时别响应,影响用户体验甚至引起ANR异常用户可能会决定退出甚至卸载您的应用。,所以耗时操作都必须在子线程执行。

子线程可以执行耗时操作但也不是越多越好,因为只有线程数量不超过cpu核心数量才会同时执行,一定那超过也是需要通过时间轮转调度而非同时进行。线程的存在即便什么也不执行也是会占用一定的内存开销并且执行创建和销毁线程的操作也会有系统开销,因此会有线程池的概念。

当应用组件启动时,如果应用没有运行任何其他组件,Android 系统会使用单个执行线程为应用启动新的 Linux 进程。默认情况下,同一应用的所有组件都在同一进程和线程(称为主线程)中运行

Android 的单线程模型有以下两条规则:

请勿阻塞界面线程。

请勿不能从界面或主线程之外的任何线程更新界面。

为了帮助您遵循这些规则,Android 提供了多种从其他线程访问界面线程的方式。以下列出了几种有用的方法:

Activity.runOnUiThread(Runnable)

View.post(Runnable)

View.postDelayed(Runnable, long)

不过,随着操作变得越来越复杂,为了便于维护可以考虑在工作器线程中使用 Handler 处理从界面线程传送的消息。

2、使用子线程

主线程我们知道是在ThreadActivity的main方法中启动的,下面总结以下怎样开启一个子线程,结合日志一起看:

先打印一下主线程的名称和id:

Log.e(TAG, "   当前线程= " + "     name="+Thread.currentThread().getName()+ "    id="+Thread.currentThread().getId());

日志打印:05-04 04:04:52.520 5942-5942/com.example.testdemo3 E/ThreadActivity:    当前线程=      name=main    id=1

第一种、通过Thread+Runable的匿名内部类实现:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        //耗时任务
        Log.e(TAG, " Thread-run  当前线程= " + "     name="+Thread.currentThread().getName()+ "    id="+Thread.currentThread().getId());
    }
});

thread.setName("俺是第一");
                if (threadIsRuning){
                    thread.interrupt();
                    threadIsRuning = !threadIsRuning;
                    // 停止一个线程
                }else {
                    thread.start();
                    threadIsRuning = !threadIsRuning;
                }

日志信息:05-04 04:04:56.661 5942-5981/com.example.testdemo3 E/ThreadActivity:  Thread-run  当前线程=      name=俺是第一    id=217

第二种 和第一种相同只是将Runnable单独拎出来,写一个类实现Runnable接口

// 定义Runnable的实现类
public static class MyRunnable implements Runnable{

    @Override
    public void run() {
        try {
            Thread.sleep(2000);
            Log.e(TAG, "Runnable-run  当前线程= " + "     name="+Thread.currentThread().getName()+ "    id="+Thread.currentThread().getId());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}


                MyRunnable myRunnable = new MyRunnable();
//                myRunnable.run();//还是当前线程
                Thread thread02 = new Thread(myRunnable);
                thread02.setName("俺是第二");
                thread02.start();

日志信息:05-04 04:05:08.664 5942-5982/com.example.testdemo3 E/ThreadActivity: Runnable-run  当前线程=      name=俺是第二    id=218

第三种 继承Thread类重写run方法

// 定义Thread类的子类
public static class MyThread extends Thread{
    //重写Thread类中的run方法,设置线程任务
    @Override
    public void run() {
        Log.e(TAG, "MyThread- Thread-run  当前线程= " + "     name="+Thread.currentThread().getName()+ "    id="+Thread.currentThread().getId());
        //run方法此处打印: 05-04 03:52:55.402 5466-5466/com.example.testdemo3 E/ThreadActivity: MyThread- Thread-run  当前线程=      name=main    id=1
        //start方法时此处打印:05-04 04:15:42.852 5942-6057/com.example.testdemo3 E/ThreadActivity: MyThread- Thread-run  当前线程=      name=Thread-219    id=219
    }
}
//启动线程
                MyThread myThread = new MyThread();
                myThread.start(); //重新启动线程
//                myThread.run();//还是在当前线程,不会重新启动线程

如果上面是执行myRunnable.run();//还是当前线程的日志信息:05-04 03:52:55.402 5466-5466/com.example.testdemo3 E/ThreadActivity: MyThread- Thread-run  当前线程=      name=main    id=1
如果上面是执行thread02.start(); //05-04 04:15:42.852 5942-6057/com.example.testdemo3 E/ThreadActivity: MyThread- Thread-run  当前线程=      name=Thread-219    id=219

这里调用了启动线程、停止线程、set和get线程名称以及id等方法。

2、Thread和Runnable什么关系

Thread就是Runnable的一个实现类:

Runnable:

Thread:

两中方式我们该怎样选,个人认为如果想简单点就用Thread想灵活一些请考虑Runnable。使用Runnable开启线程也离不开Thread,如果觉得调用runnable的run方法也可以开启线程执行任务就有点跑偏了。

先看一下Thread类的start()方法和run()方法的不同就理解为什么说跑偏了。

Thread类的start()方法和run()方法的主要区别在于人家扮演的角色压根就不一样。以下是两者的主要区别:

首先达到的效果不同。run()方法是线程中实际运行的代码,他就是仅代表调用run方法而已;start()方法是启动一个新线程,它会创建一个新的线程并在新的线程中并行执行run方法的任务。因此在实际的多线程编程中,应该通过调用start()方法来启动线程。

启动次数不同。run()方法是一个类中的普通方法,可以多次调用,每次调用都会执行一次任务,而start()方法只能被调用一次,如果再次调用start()方法,会抛出IllegalThreadStateException异常。//todo 每次停止线程,再启动需要重新new一个线程,否则报错:    java.lang.IllegalThreadStateException    at java.lang.Thread.start(Thread.java:878)     at com.example.testdemo3.activity.ThreadActivity$2.onClick(ThreadActivity.java:62)

所以runnable的run方法和Thread类的run()方法一样,run()方法是线程中实际运行的代码,他就是仅代表调用run方法而已,我们还是需要通过Thread的start方法开启新线程执行任务。

  • 30
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android实现线程的几种方式有以下几种方法。首先,可以使用Java的Thread类来创建和管理线程。这种方式需要手动创建线程对象,并在run()方法中编写线程的逻辑。其次,可以使用Java的Runnable接口来实现线程。这种方式需要创建一个实现了Runnable接口的类,并将其作为参数传递给Thread类的构造函数。然后,可以使用Handler类来实现线程。通过Handler的post()或postDelayed()方法,可以将任务添加到主线程的消息队列中,从而在主线程中执行异步操作。此外,还可以使用AsyncTask类来实现线程。AsyncTask是Android提供的一个封装好的线程池,可以方便地在后台执行耗时操作,并在主线程中更新UI。通过重写AsyncTask的doInBackground()方法来执行耗时操作,并通过onPostExecute()方法来更新UI。需要注意的是,AsyncTask必须在主线程中创建实例,并且execute()方法也必须在主线程中调用。另外,Android 3.0之后,可以使用executeOnExecutor()方法来实现并发执行多个AsyncTask任务。总结起来,Android实现线程的几种方式包括使用Thread类、Runnable接口、Handler类和AsyncTask类。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [Android——多线程的多种实现方式](https://blog.csdn.net/abliudede/article/details/104891324)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Android开发中四种常用的多线实现方式](https://blog.csdn.net/star_nwe/article/details/130140238)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值