Android多线程

JAVA-Android-多线程实现方式及并发与同步,面试安卓卡顿 说一千道一万,不如自己去行动。要想在移动互联网的下半场是自己占有一席之地,那就得从现在开始,从今天开始,马上严格要求自己,既重视业务实现能力,也重视基础和原理。基础夯实好了,高楼才能够平地而起,稳如泰山。最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。还有。 阅读详情

Android 多线程

本篇博客将涉及以下内容:

  • 多线程概述
  • Thread类和Runnable接口
  • 线程相关(线程生命周期、线程控制、线程同步)
  • 线程通信(Handler机制)
  • Android线程的创建和使用
  • 线程池概念和使用

多线程概述

众所周知,为了提供流畅的UI效果,Android系统不允许在主线程进行耗时操作,所以我们会经常使用多线程进行耗时操作,进行网络操作,数据的保存什么的。这是一个Android开发者一定会频繁接触到的知识模块,因此,为了更好的使用多线程,我们有必要把它弄清楚,弄明白。在我所阅读的很多很多书籍中,都将多线程的使用讲的非常明白,AsyncTask、HandlerThread、IntentService等等,讲的都很好,我就不在这方面多费口舌了。
理一理思路吧:

  • 为什么使用多线程?

Android中一个应用默认对应一个进程,一个进程有一个默认的线程(主线程/main线程/UI线程),为了处理耗时任务,我们创建新的工作线程来处理任务,并且将处理结果告知或者不告知主线程。

  • 如何创建线程?

可以在任何一个线程中创建新的线程,新线程和创建线程的线程之间没有父子关系。Java中代表线程的类有且仅有Thread类,所以创建一个新线程的方法只有一种,就是创建一个Thread类的实例。

我想肯定有人会问为什么你说只有一种,因为我觉得其他的都是变种。这些变种提供其他一些特定的功能、或者加入了一些其他的方法,都是为了方便开发者的使用。

所以我认为核心就是一种。

  • 继承Thread类可以加入任何你想要增加的功能。
  • 实现Runnable接口也只是为Thread提供了一个运行方法
  • Callable接口只是为了拓宽Runnable,使新线程增加可以返回数据的能力。
  • AsyncTask类提供了线程池和handler机制,方便后台任务更新UI。
  • HandlerThread继承自Thread加入了Handler机制,其他线程将要处理的任务集中到HandlerThread中进行处理。
  • IntentService继承自Service类,加入HandlerThread机制,提供线程优先级,不容易被系统杀死。

我要表达的是,万变不离其宗,只有详细理解Thread,才能更好的掌握他们常说的那几种使用线程的方法。使用嘛,看看文档就会,重要的是理解为什么是这样。

Thread类和Runnable接口

Thread类是Runnable接口的实现类,其中run()方法判断了当前Thread对象是否含有runable实例,如果有,调用该实例的run方法。
这俩是创建多线程最最核心的内容,Runnable最简单,先说Runnable

Runnable接口

Runnable接口定义了一个run方法,其他什么都木有。

Runnable类只是提供了一个统一的规则/协议,给那些需要有代码需要运行的对象使用。
This interface is designed to provide a common protocol for objects that
wish to execute code while they are active. For example,
Runnable is implemented by class Thread.

Thread类

Thread类是Java中线程类,使用Thread类或者其子类可以创建一个线程对象。
Thread类中一些重要的参数(可能还有其他的重要的):

  • ThreadGroup group:线程组,默认和创建该线程的线程在同一个线程组。
  • Runnable target:有代码需要运行的对象,除了Thread的子类,其他时候需要提供,否则该线程run方法中没有可运行的代码。
  • boolean daemon :是否是后台线程,默认和创建该线程的线程一样。
  • int priority:线程优先级,默认和创建该线程的线程一样。
  • String name:线程名,默认为Thread-n,n会自己变。
  • int threadStatus:线程当前的状态。
  • long stackSize:线程运行需要的栈内存大小,可以不指定

创建线程时,可以指定以上参数。创建线程后调用start()方法启动线程,java虚拟机会自动在合适的时间调用线程的run方法。

    /**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     */

    public void run() {
        if (target != null) {
            target.run();
        }
    }

如果创建线程的时候提供了Runable对象(target参数),则调用Runnable对象的run方法, 否则,该线程什么都不做。
这也就为什么很多人说,创建线程有两个方法的原因:一是继承Thread类重写run方法;二是新建一个实现了Runnable接口的类,创建该类的实例,传入Thread的构造方法中。

线程相关(线程生命周期、线程控制、线程同步)

线程生命周期

Java虚拟机中线程的生命周期和操作系统中线程生命周期,没有映射关系,操作系统中线程状态的切换可以去看再熟悉一下操作系统相关知识,这里贴一个图先看看:

操作系统中线程状态

线程从创建到结束会经历一系列生命周期:

  • NEW
  • RUNNABLE
  • BLOCKED
  • WAITING
  • TIMED_WAITING
  • TERMINATED
    /**
     * A thread can be in only one state at a given point in time.
     * These states are virtual machine states which do not reflect
     * any operating system thread states.
     * 线程的状态是相对于Java虚拟机的,和系统中线程的状态没有对应关系
     */
    public enum State {
        /**
         * 还没有调用start方法的线程
         */
        NEW,

        /**
         * 可以运行但是不一定在运行,可能在等待其他的一些资源,如CPU
         */
        RUNNABLE,

        /**
         * BLOCK 和 WAITING  结合起来更加好理解一点
         * 一旦一个线程调用了Object.wait、Thread.join或者Locksupport.park方法,
         * 线程进入WAITING状态,
         * 进入WAITING状态的线程需要等待其他线程的唤醒,唤醒成功后并不一定马上可以执行(RUNNABLE)
         * 线程进入BLOCK状态,等待执行机会的到来。
         * 
         */
        BLOCKED,
        WAITING,

        /**
         * 调用下列方法后进入TIMED_WAITING状态
         * Thread.sleep
         * Object.wait(long)
         * Thread.join(long)
         * LockSupport.parkNanos
         * LockSupport.parkUntil
         * 
         */
        TIMED_WAITING,

        /**
         * 线程执行结束。
         */
        TERMINATED;
    }

线程控制

线程的执行通常是由系统调用的,开发者并不能准确指定线程什么时候运行,但是还是可以通过一些设定来对线程进行适当的控制。

  1. setPriority(int range 1-10):改变线程的优先级,使其获得更多的执行机会。

    提供三个优先级:

    /**
     * The minimum priority that a thread can have.
     */
     public final static int MIN_PRIORITY = 1;
    /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;
    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

    默认优先级和创建该线程的线程的优先级相同,范围为1-10,建议使用1-5-10 这三个

  2. join:在A线程的run方法中调用B.join,在A线程阻塞,直到B线程执行完成。

  3. sleep:使线程进入睡眠状态,在睡觉的时候,不会被唤醒,睡醒之后进入RUNNABLE状态,等待执行
  4. yeild:暂停当前线程的执行,让CPU重新进行调度。给同等或者更高优先级的线程让步,如果没有这样的线程,该线程会被重新执行。
  5. setDaemon():设置为后台线程的线程们不能单独存在,当所有非后台线程执行结束,后台线程会自己结束。
  6. wait:在线程同步中使线程等待,直到被唤醒。
  7. park,unpark:将线程暂时挂起,直到某个时间或者直到被唤醒。

线程同步

跟上面说的一样,由于线程的执行是不可控的,所以如果不加以控制,总是可能会发生错误的。相信开发过程中肯定遇到需要一个在Adapter中更新显示的数据,同时网络罗数据返回时修改Adapter中绑定的数据,这个时候如果使用ArrayList一不小心就会发生错误。所以我们需要了解线程同步,在需要的时候使用线程同步,避免错误的发生。锁机制还是比较复杂的,这里这是介绍一下大概的内容。
线程同步总是会有性能问题,只在必要的时候使用线程同步。
线程同步主要涉及以下内容:

synchronized

使用synchronized关键字对方法或者代码块增加同步锁,在执行结束(正常结束、return、发生异常和错误)或者在同步块内调用wait(sleep,yeild不会释放)方法之后,同步锁自动释放。

    //下面示例代码是来自Thread类中用到的synchrozined
    //启动线程
    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * 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.
         *
         * A zero status value corresponds to state "NEW".
         */
        // Android-changed: throw if 'started' is true
        if (threadStatus != 0 || started)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        started = false;
        try {
            nativeCreate(this, stackSize, daemon);
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    //设置线程优先级
    public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            synchronized(this) {
                this.priority = newPriority;
                if (isAlive()) {
                    nativeSetPriority(newPriority);
                }
            }
        }
    }
Lock

显式地新建一个Lock对象,在适当的位置进行lock,释放锁时直接调用unLock即可释放锁资源。Lock比synchronized好用的一点是对于同一个资源,可以显式地控制锁之间的互斥关系,增强程序执行效率。比如常见的读写锁,readLock和writeLock,readLock和readLock之间不互斥,两个进程都可以随时读取,这在一定程度上提高了效率。

//我们直接来看使用示例:
//对读操作添加readLock,
//当其他的线程需要写数据的时候,需要等待当前线程锁的释放,
//当其他线程需要读数据时可以直接读,不受其他readLock的影响
//
//对写操作添加writeLock,
//禁止其他线程对数据进行读写。直到当前线程释放。
class RWDictionary {
    private final Map<String, Data> m = new TreeMap<>();
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();

    public Data get(String key) {
        r.lock();
        try {
            return m.get(key);
        } finally {
            r.unlock();
        }
    }

    public List<String> allKeys() {
        r.lock();
        try {
            return new ArrayList<>(m.keySet());
        } finally {
            r.unlock();
        }
    }

    public Data put(String key, Data value) {
        w.lock();
        try {
            return m.put(key, value);
        } finally {
            w.unlock();
        }
    }

    public void clear() {
        w.lock();
        try {
            m.clear();
        } finally {
            w.unlock();
        }
    }
}

线程通信(Handler机制)

Android中线程之间的通信主要就是消息机制(Handler机制)。
消息机制广泛应用在Android中的UI更新,但是其实他能做的可远远不止UI的更新,对于多任务的集中式处理也是消息机制的一大应用场景,同时对于AysncTask、HandlerThread、IntentService等等而言,内部也都使用了消息机制。

消息机制的基本原理是:

使用一个MessageQueue来作为连接两个线程的中介
(线程资源放在栈内存区、MessageQueue放在堆内存中,同一进程的不同线程可以访问同一块堆内存),
两个线程都可以对MessageQueue进行操作,从而实现线程的通信。

具体的实现逻辑是:

  1. 在线程A中创建Looper并且开始消息的循环,
  2. 利用A中创建好的Looper创建一个Handler(Handler的构造函数中默认使用当前线程的Looper)
  3. 新重建一个线程B,让线程B持有Handler的引用,
  4. 在线程B中处理任务,通过Handler发送消息对象Message将处理结果传递出去(通过handler.post(Runnable)或者handler.sendMessage(Message)等方法,发送的消息最终都会被包装成一个Message对象,如post(Runnable)方法会首先创建一个Message,然后将Runnable作为Message中的Runnable callback实例变量)
  5. Message最终会放入MessageQueue中,并且被线程A的Looper获取到
  6. Looper获取到Message之后,根据Message中保存的Handler的引用,调用Handler的dispatchMessage()方法,如果Message中包含callback,则执行callback的run()方法,否则调用handleMessage()方法处理消息。
  7. 流程结束之后调用looper.quit()关闭Looper。

关于消息机制的详细内容,可以回头翻一下之前在郭霖公众号里我的一片投稿,也可以点击文章最后的查看原文通过链接访问:
Android消息机制的文章链接为:http://blog.youkuaiyun.com/code__man/article/details/79108191

Android线程的创建和使用

Android中线程的跟Java一样,使用Thread类代表线程。创建Thread类有以下几种方法:

  • 继承Thread类,重写run方法,创建子类实例,调用Thread.start()方法。
//step1:定义自己的Thread,也可以直接用局部类实现

package com.nullponinter.study.ThreadsTest;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Created by Francis on 15/01/2018.
 */

public class MyThread extends Thread {
    private static final String TAG = "MyThread";

    @Override
    public void run() {
        super.run();
        //你可以在这里
        //写这个需要处理的逻辑
    }

    @Override
    public void destroy() {
        super.destroy();
    }
}

//Step2: 直接使用
MyThread myThread = new MyThread();
myThread.start();
  • 在Thread类的构造方法中传入一个实现Runnable接口的类的实例,作为Thread.target变量,在run方法中调用。

    这里又可以分为两种情况:

    • 直接实现Runnable接口,重写run方法,传入构造方法
    • 使用FutureTask作为Runnable对象(在FutureTask的构造方法中传入Callable对象),将FutureTask作为Thread.target变量。
    • 你也可以根据自己的需要,去随便对Runnable做点什么
    /**
     * 测试futureTask
     */
    public static void testThread() {


        //使用Runable
        new Thread(new Runnable() {
            @Override
            public void run() {
                //do something

            }
        }).start();

        //使用FutureTask
        final FutureTask<Long> futureTask = new FutureTask(new Callable<Long>() {
            @Override
            public Long call() throws Exception {
                Long sum = 0L;
                for (int i = 0; i < 100000000; i++) {
                    sum += i;
                }
                return sum;
            }
        });


        new Thread(futureTask).start();
        try {
            Log.e("====sum=", "llllll");
            Long sum = futureTask.get();
            Log.e("====sum=", sum + "");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
  • 使用AsyncTask类,在doInBackground方法中做耗时操作,调用更新进度方法,返回操作结果等、在onProgressUpdate方法中更新进度、在onPostExecute方法中操作返回结果,更新UI。
    /**
     * 测试asyncTask的方法
     */
    private void testAsyncTask() {

        AsyncTask asyncTask = new AsyncTask<String,Integer,String>(){

            @Override
            protected String doInBackground(String... strings) {
                return null;
            }

            @Override
            protected void onPreExecute() {
                super.onPreExecute();
            }

            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);
            }

            @Override
            protected void onProgressUpdate(Integer... values) {
                super.onProgressUpdate(values);
            }
        };

        asyncTask.execute("");
    }
  • 使用HandlerThread,将特定的任务集中到一个线程来做,其他线程将任务发送到HandlerThread。
    /**
     * 测试HandlerThread
     */
    private void testHandlerThread() {

        //使用的Handler构造方法没有提供Looper,默认使用当前线程的Looper,
        // 所以回调方法中的代码也在当前线程执行
        final Handler currentThreadHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                StringBuilder sb = new StringBuilder("currentThreadHandler")
                        .append("\nmessage what=").append(msg.what)
                        .append("\nthreadName=").append(Thread.currentThread().getName());
                Log.e("===", sb.toString());
                return false;
            }
        });

        HandlerThread handlerThread = new HandlerThread("MyTestThread");
        handlerThread.start();
        //使用的Handler构造方法提供了handlerThread的Looper,
        // 所以回调方法中的代码在handlerThread中执行,
        //该线程是工作线程,不能用于更新UI
        final Handler handlerThreadHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                StringBuilder sb = new StringBuilder("handlerThreadHandler")
                        .append("\nmessage what=").append(msg.what)
                        .append("\nthreadName=").append(Thread.currentThread().getName());
                Log.e("===", sb.toString());
                return false;
            }
        });

        final Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                int random = Math.abs(new Random().nextInt());
                int sum = 0;
                for (int i = 0; i < random; i++) {
                    sum = sum + i;
                }

                if (sum % 2==0) {
                    handlerThreadHandler.sendEmptyMessage(sum % 2);
                }else {
                    currentThreadHandler.sendEmptyMessage(sum % 2);
                }
            }
        });

        thread.start();
        thread.start();
        thread.start();
        thread.start();
        thread.start();
    }
    //输出的结果有两种情况

    //handlerThreadHandler
    //message what=0
    //threadName=MyTestThread
    //或
    //currentThreadHandler
    //message what=1
    //threadName=main
  • 继承IntentService,重写onHandleIntent方法处理Intent,其他线程直接startService(Intent)就可以了。

//Step1 实现自己的IntentService类
package com.nullponinter.study.ThreadsTest;

import android.app.IntentService;
import android.content.Intent;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * Created by Francis on 15/01/2018.
 */

public class MyIntentService extends IntentService {
    public MyIntentService() {
        super("MyIntentService");
    }
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public MyIntentService(String name) {
        super(name);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("===","IntentService.onCreate() runs on "+Thread.currentThread().getName());

        Log.e("===","IntentService被创建");

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("===","IntentService被销毁");

    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {

        Log.e("===","intent service onHandleIntent run on "+Thread.currentThread().getName());
        int randomCount = (int) Math.random()%100;

        int sum = 0;
        for (int i = 0; i < randomCount; i++) {
            sum += i;
        }
        Log.e("===","sum==="+sum);

    }
}

//Step2 使用IntentService
private void testIntentService() {
     for (int i = 0; i < 3; i++) {
        Intent intent = new Intent(this, MyIntentService.class);
        startService(intent);
    }
}

这几种方式各有各的好处,可以根据自己的需要选择不同的实现。具体的使用方法,网上的同类教程太多了,没有写的必要了。

线程池概念和使用

众所周知的是线程的创建、销毁都是需要系统帮我们做很多事情的。频繁的创建、销毁线程对系统消耗是比较很大的,为了避免系统资源的浪费,对于生命周期不长,需要频繁创建销毁的线程,我们都建议使用线程池来进行线程的管理。
在前面我们提到过:

Runnable类提供了一个统一的规则/协议,给那些需要有代码需要运行的对象使用。
This interface is designed to provide a common protocol for objects that
wish to execute code while they are active. For example,
Runnable is implemented by class Thread.

既然Runnable代表一个可以执行的内容,那么事实上,我们可以创建一个线程,多个Runnable对象使用同一个Thread对象进行Runnable中run方法体的执行。线程池的原理正基于此。(Callable也是一样的)

相关概念

涉及到的相关类简介:

//继承关系介绍:
* public interface Executor
* public interface ExecutorService extends Executor
* public abstract class AbstractExecutorService implements ExecutorService
* public class ThreadPoolExecutor extends AbstractExecutorService
* public class Executors
  • Executor:线程池的基础接口,只有一个void execute(Runnable command)方法,方法是具体实现由子类去完成。
  • ExecutorService:Executor的子接口,提供了几个submit方法,拓展execute方法,提供了shutdown方法关闭池,提供几个invoke方法执行Callable列表。
  • AbstractExecutorService:实现ExecutorService接口的抽象类,开发者可以创建该类的子类创建自己的线程池实现。
  • ThreadPoolExecutor:继承自AbstractExecutorService,提供了很多可供配置的参数,为开发提供便利,用来创建真正的线程池的类。
  • Executors:工厂类,将ThreadPoolExecutor设一个默认值,允许开发者提供极少的参数即可创建出可满足大多数情况的线程池的实现。

ThreadPoolExecutor中的参数

    /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     *        池中核心线程的数量
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     *        最大线程数量
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     *        非核心线程的存活时间,
     *        如果非核心线程在等待这么久之后还没有任务执行,就要死掉了
     * @param unit the time unit for the {@code keepAliveTime} argument
     *       存活时间的单位
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     *        任务的等待队列,定义了需要等待执行的线程应该放在哪里,
     *        以及重新取出来执行的规则是什么
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     *        创建新线程的线程工厂
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     *        饱和策略
     *        当新任务到达但是池中数量已达到最大值,
     *        并且任务队列也已经满了的情况下,
     *        任务拒绝的处理
     *        默认的处理方式是直接抛出异常
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

几种常用的线程池

Executors工厂类一共提供了很多种(大概12种)new开头的方法供使用,大致可以分成下面几类:

  • newCachedThreadPool:

该池核心线程数为0,最大线程数没有上限,队列中不保存任务,线程的存活周期是60s
所以,该池适合于处理生存期很短的异步任务
SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态

    /**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available.  These pools will typically improve the performance
     * of programs that execute many short-lived asynchronous tasks.
     * Calls to {@code execute} will reuse previously constructed
     * threads if available. If no existing thread is available, a new
     * thread will be created and added to the pool. Threads that have
     * not been used for sixty seconds are terminated and removed from
     * the cache. Thus, a pool that remains idle for long enough will
     * not consume any resources. Note that pools with similar
     * properties but different details (for example, timeout parameters)
     * may be created using {@link ThreadPoolExecutor} constructors.
     *
     * @return the newly created thread pool
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  • newFixedThreadPool:

核心线程数等于最大线程数,任务数超过线程数时放入阻塞队列中等待执行
合适于数量相对稳定,时间较长的任务

     /**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue.  At any point, at most
     * {@code nThreads} threads will be active processing tasks.
     * If additional tasks are submitted when all threads are active,
     * they will wait in the queue until a thread is available.
     * If any thread terminates due to a failure during execution
     * prior to shutdown, a new one will take its place if needed to
     * execute subsequent tasks.  The threads in the pool will exist
     * until it is explicitly {@link ExecutorService#shutdown shutdown}.
     *
     * @param nThreads the number of threads in the pool
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
  • newScheduledThreadPool:

>

核心线程数自定义,最大线程数没有上限,10s的保活时间
DelayedWorkQueue:

    /**
     * Creates a thread pool that can schedule commands to run after a
     * given delay, or to execute periodically.
     * @param corePoolSize the number of threads to keep in the pool,
     * even if they are idle
     * @return a newly created scheduled thread pool
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     */
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }
  • newSingleThreadExecutor:

单例线程,任意时间池中只能有一个线程

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
Android智能聊天机器人的实现,安卓多线程面试题 android:layout_width=“50dp” android:layout_height=“50dp” android:src="@drawable/shu" /> <TextView android:id="@+id/left_name" android:layout_width=“wrap_content” android:layout_height=“wrap_content” android:layout_marginLeft=“12dp” android:text=“小洛” 阅读详情

相关推荐

Cadence公司的数字IC设计工具:综合工具(Genus)——(2)

介绍Cadence公司的数字IC设计工具Genus

qq_42922513的博客 1944

android synchronized 方法,【并发】android中的synchronized

synchronized用于多线程访问,并且被修饰的部分不能同时被执行,是代码同步的一种方式。1 使用synchronized修饰方法1.1 synchronized修饰方法原理过程:当多个线程同时访问被synchronized修饰的方法是,有且仅有一个线程可以被访问,当一个线程在访问时,其它线程只能等待。当一个线程访问完毕后,下一个线程才可以访问。原理:当方法被synchronized修饰后,如...

weixin_29189003的博客 3615

使用MID360在室内快速定位

在预构建的点云地图中实现实时3D全球定位。通过融合FAST-LIO的低频全局定位(约0.5~0.2Hz)和高频里程计,整个系统的计算效率很高。消除里程计的累计误差初始定位可以通过RVIZ的粗略手动估计或来自另一个传感器/算法的姿势来提供。

CTwinkle的博客 1162

Android---Synchronized 和 ReentrantLock

Java中两个实现同步的方式synchronized和ReentrantLock。synchronized使用更简单,加锁和释放锁都是由虚拟机自动完成。ReentrantLock需要开发者手动去完成,很Reentrantl ock的使用场景更多 公平锁和读写锁都可以在复杂场景中发挥重要作用。

qq_44950283的博客 1539

二元交叉熵:分类模型概率校准的数学根基

二元交叉熵(BCE)是二分类任务中实现概率校准的核心损失函数,其本质源于最大似然估计与KL散度,确保模型输出具备真实可解释的概率意义。相比均方误差(MSE)或Hinge Loss,BCE强制优化预测分布与真实伯努利分布之间的信息差异,使p=0.8真正对应约80%的正例发生率,支撑风控、医疗、推荐等高信度场景的阈值决策。它不只影响训练收敛,更决定部署后概率的业务可信度——这正是Focal Loss等变体无法替代其基础地位的根本原因。理解BCE,就是理解分类模型如何从‘打分器’进化为‘概率契约签署者’。

congli3478的博客 1333

C++和蓝图1:Unreal Engine 中 C++ 与蓝图的桥梁

摘要: 本文介绍了Unreal Engine(UE)中C++与蓝图可视化脚本的结合机制,重点解析了宏系统(UCLASS、UPROPERTY、UFUNCTION)如何实现代码与蓝图的交互。相比Unity的纯C#脚本模式,UE的双轨制开发允许程序员用C++处理核心逻辑,同时通过蓝图让非程序员快速迭代。文章对比了两大引擎的编程范式差异,指出UE的反射系统和宏标记提供了更精细的控制,并给出了从Unity迁移到UE的具体思路。核心在于理解UE的混合编程优势:C++保证性能,蓝图提升开发效率,二者通过宏系统无缝协作。

qq_46348216的博客 1407

安卓中synchronized 关键字 的作用和介绍

在Java中,关键字用于控制多线程环境下对方法或代码块的访问,确保在同一时刻只有一个线程可以执行该方法或代码块。当你在方法声明中使用关键字,这意味着任何线程在调用该方法之前必须获得该方法所属对象的锁。这通常用于保护共享资源的一致性,防止因多线程同时修改同一数据而导致的数据不一致或竞态条件。在Java中,关键字是用来控制多个线程访问共享资源的一种机制,确保在同一时刻只有一个线程可以执行特定段代码。这主要是用来解决多线程环境下的线程安全问题。使用和。

qq_33386162的博客 1706

华为OD机试双机位C卷-简单的解压缩算法(C/C++/Py/Java/Js/Go)

华为OD机试(机考)双机位C卷 【简单的解压缩算法】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD真题【简单的解压缩算法】。

qq_45776114的博客 475

Android开发中synchronized的实现原理

**1.修饰实例方法,**作用于当前实例加锁,进入同步代码前要获得当前实例的锁。没有问题的写法: 因为这段代码中只输入了一个AccountingSync实例。下面是有问题的写法: 在上述代码中出现了一个严重的错误,虽然我们使用了sychronized修饰了increase方法,但new了两个不同的实例对象,这就意味着会出现两个不同的实例对象锁,这样的话ti和t2都会进入各自的对象锁,因此线程安全就得不到保证。想要解决这个问题,就需要将sychronized作用于静态的increase方法。2.修饰静态方法,

m0_70749039的博客 1018

Python 爬虫实战:赛事分析平台构建与比赛数据抓取

在体育赛事日益丰富的今天,对比赛数据的收集与分析对于运动员、教练团队以及体育爱好者都具有重要意义。通过构建一个赛事分析平台,我们可以系统地抓取、整理和展示各类比赛数据,为用户提供全面的赛事洞察。本文将深入讲解如何利用 Python 爬虫技术实现这一平台的构建,涵盖从目标网站分析到数据可视化展示的全过程。

u014481728的博客 3180

VTK API 详解(134):vtkOBJWriter - 写入 OBJ 模型

是 VTK 中用于将数据写入 Wavefront OBJ 文件格式的类。OBJ 格式是广泛支持的 3D 网格模型格式,支持顶点、面和基本几何信息的保存,便于模型在多种三维软件间交换。

yumeiguo的博客 140

Android中synchronized机制详解

JAVA中synchronized关键字能够作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。假如再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。 在进一步阐述之前,我们需要明确几点: A.无论synchronized关键字加在方法上还是对象上,他取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。 B...

qq_25330791的博客 2847

在 VS Code 里配置 SAP Joule for Developers 的 ABAP AI side-by-side 能力

摘要: 随着ABAP开发工具链的演进,VS Code逐渐融入ABAP开发流程,SAP Joule for Developers的ABAP AI能力通过side-by-side模式(基于BTP中央服务)为多系统开发提供统一AI支持。该模式避免了各ABAP后端独立升级AI能力的碎片化问题,由管理员配置BTP订阅URL后,开发者可在ADT或VS Code中全局调用。实际落地需注意角色分工(管理员配置服务,开发者填写URL)、AI units资源规划及配置细节(如区分全局与项目级设置)。建议将Joule URL纳入

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 120

Android多线程实现的几种方式_安卓多线程

目前已经更新的部分资料:网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。需要这份系统化学习资料的朋友,可以戳这里获取一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**需要这份系统化学习资料的朋友,可以戳这里获取。

erhtre的博客 633

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

【Java转Android】45. 使用安卓多线程

package nopi.aystudio.mthread; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import androi...

技术宅-Nopi的博客 190

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Android(安卓)多线程原理理解

为什么我们需要使用多线程 提高用户体验 避免ANR(Application is not responding)

Joy_yang17的博客 990

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

安卓app开发系列之-多线程编程

多线程编程是提高Android应用性能和用户体验的关键。本文介绍了Android中实现多线程的方法,包括基本的线程管理类和高效的线程池方式,以及现代Kotlin协程的使用。尽管多线程编程为开发带来了好处,但也引入了复杂性,需要开发者特别注意竞争条件、内存泄漏等问题。随着对多线程技术的深入理解,开发者能够更有效地管理应用行为,以提升用户的满意度和应用的性能。如果有进一步的问题或需要探讨的内容,请随时提出!

Jamson 1368

视频教程-安卓开发多线程精讲一学就会-Android

安卓开发多线程精讲一学就会 大学计算机专业毕业,从业IT四年,擅长java,...

weixin_32476185的博客 172

Android多线程学习和总结

一、Android中实现多线程的方式 1. Java多线程 常见的三种形式 继承Thread类,重写该类的run()方法 class MyThread extends Thread { private int i = 0; @Override public void run() { for (i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() +

九万里云烟的博客 495

安卓多线程

安卓多线程一.前言二.线程的生命周期三.多线程的几种实现方法四.终止线程的方法五.跨线程更新UI六.Handler多线程1.异步信息处理机制2.Handler介绍3.简单的Handler示例4.Handler优缺点 一.前言 Android是单线程模型,这意味着Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行,所以你单纯的new一个Thread并且start()是不行的,因...

weixin_44275194的博客 848

Android高工:细说 Android 多线程,探究原理知其所以然,2024大厂安卓知识点总结

*要想成为高级安卓工程师,必须掌握许多基础的知识。**在工作中,这些原理可以极大的帮助我们理解技术,在面试中,更是可以帮助我们应对大厂面试官的刁难。《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!**由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

2401_84148942的博客 1040

java和安卓的主线程的区别_java多线程和android平台下的多线程

一、多线程的概念多线程即是进程的多个执行路径,但并不能理解为这几个路径是同时执行的。在单cpu下应该是根据优先级调度法则轮流给这些个线程的执行时间片。二、Java中的多线程1)、如何创建一个线程两种方式,实现Runnable接口和继承Thread类。一般建议使用Runnable接口。2)、join、yield、sleep关键字的含义Join即是将某一线程插入到当前线程中执行;yield使得与当前正...

weixin_42501892的博客 209

安卓——多线程

安卓的多线程和java的多线程操作一样 有两种实现方法: 继承Thread类,重写run方法 实现Runnable接口,重写run方法 两种方法的线程启动需要调用start方法而不是run,否则相当于一个普通方法而非线程 继承Thread类:直接new然后调用start方法 //第一种实现子线程的方法:继承Thread类 private class MyThread extends Thread{ @Override public void r

weixin_43795939的博客 236

安卓M3U8多线程批量下载神器

本文详细介绍安卓M3U8多线程批量下载神器的核心功能与使用技巧。该工具针对M3U8视频格式深度优化,支持智能断点续传、多线程加速和批量下载管理,大幅提升下载效率和稳定性,是教育学习、媒体素材收集和个人娱乐的得力助手。

d6e7f8的博客 1051

Android多线程进度条,Android编程开发实现带进度条和百分比的多线程下载

本文实例讲述了Android编程开发实现带进度条和百分比的多线程下载。分享给大家供大家参考,具体如下:继上一篇《java多线程下载实例详解》之后,可以将它移植到我们的安卓中来,下面是具体实现源码:DownActivity.java:package com.example.downloads;import java.io.File;import java.io.IOException;import ...

weixin_31022521的博客 346

2024年Android多线程实现的几种方式_安卓多线程,2024年最新h5加载页面素材

上面应该是比较常见的两种实现多线程的方式,而这里是通过Handle来实现的,可能有人会问,为什么android多线程的需要handle?handle有什么作用?其实handle大有作为了,它不仅可以作为信息传递的媒介和好帮手,同时也是多线程的一把好手。在 Android 中,多线程编程通常涉及到线程之间的通信和消息传递。Handler 是 Android 提供的一个机制,用于线程之间的消息传递和处理。

2401_84537826的博客 404
上一篇: 项目中一些bug的修复
下一篇: 浅析equals与HashCode
ApiPlayer
博客等级 码龄12年 34粉丝 17原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值