Java中的线程基础知识

Java中的线程基础知识

1、线程概念

线程是程序运行的基本执行单元。当操作系统(不包括单线程的操作系统,如微软早期的DOS)在执行一个程序时,会在系统中建立一个进程,而在这个进程中,必须至少建立一个线程(这个线程被称为主线程)来作为这个程序运行的入口点。因此,在操作系统中运行的任何程序都至少有一个主线程
线程的Java抽象内存模型如下,由此可见
1)每个线程都有自己独立的工作内存
2)线程1无法访问线程2的工作内存
3)线程在访问共享数据时,会把主内存中的共享变量复制到自己的工作内存中,线程操作的是工作内存中数据的副本

在这里插入图片描述

2、Java中线程实现的方式

方式一:实现 Runnable 接口

class MyThread implements Runnable{ // 实现Runnable接口,作为线程的实现类
	private String name ; // 表示线程的名称
	public MyThread(String name){
	this.name = name ; // 通过构造方法配置name属性
}
	public void run(){ // 覆写run()方法,作为线程 的操作主体
	for(int i=0;i<10;i++){
		System.out.println(name + "运行,i = " + i) ;
	}
}
};
public class RunnableDemo01{
	public static void main(String args[]){
	MyThread mt1 = new MyThread("线程A ") ; // 实例化对象
	MyThread mt2 = new MyThread("线程B ") ; // 实例化对象
	Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
	Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
	t1.start() ; // 启动多线程
	t2.start() ; // 启动多线程
	}
};

方式二:继承 Thread 类

class MyThread extends Thread{ // 继承Thread类,作为线程的实现类
	private String name ; // 表示线程的名称
	public MyThread(String name){
	this.name = name ; // 通过构造方法配置name属性
	}

	public void run(){ // 覆写run()方法,作为线程 的操作主体
	for(int i=0;i<10;i++){
	System.out.println(name + "运行,i = " + i) ;
	}
}
};

public class ThreadDemo02{
	public static void main(String args[]){
	MyThread mt1 = new MyThread("线程A ") ; // 实例化对象
	MyThread mt2 = new MyThread("线程B ") ; // 实例化对象
	mt1.start() ; // 调用线程主体
	mt2.start() ; // 调用线程主体
	}
};

两者的区别和联系:
1)Thread类也是Runnable接口的子类
2)Thread是类,而Runnable是接口。类和接口区别,类只能继承一次,而接口可以实现多个
3)最重要的分享资源功能,一般我们使用多线程就是快速解决资源问题。Runnable可以实现资源分享,类实现Runnable并不具备线程功能,必须通过new Thread(runabble子类)调用start()启动线程,所以我们通常new一个runnable的子类,启动多个线程解决资源问题。Thread是类所以我们每次new一个对象时候资源已经实例化了,不能资源共享,Thread类要实现资源共享,可以声明变量为static,类共享的可以解决。

3、线程状态及切换

Java中线程的状态可分为以下6种,也有归纳为5种的,但逻辑是类似的
1)初始(NEW):新创建了一个线程对象,但还没有调用start()方法
2)运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)
3)阻塞(BLOCKED):表示线程阻塞于锁
4)等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)
5)超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回
6)终止(TERMINATED):表示该线程已经执行完毕

在这里插入图片描述

改变线程状态的几个方法如下,
1)Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式
2)Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间
3)thread.join()/thread.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程一般情况下进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join是基于wait实现的
4)obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒
5)obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程
6)LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines), 当前线程进入WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒

4、线程优先级

最低优先级 1:Thread.MIN_PRIORITY
最高优先级 10:Thread.MAX_PRIORITY
普通优先级 5:Thread.NORM_PRIORITY
使用setPriority(Thread.MIN_PRIORITY)方法设置线程优先级。一般优先级较高的线程先执行run()方法,但是这个不是确定的。因为Java使用的是抢占式调度模型,线程的优先级具有 “随机性”,所以设置了高优先级的线程会占用更多资源,总体上会优先执行

5、线程同步

1)同步方法
用synchronized关键字修饰方法。 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态
2)同步代码块
用synchronized关键字修饰语句块。被该关键字修饰的语句块会自动被加上内置锁,从而实现同步
同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可
3)Volatile
a.volatile关键字为域变量的访问提供了一种免锁机制
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
4)使用重入锁实现线程同步
在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力
5)ThreadLocal方式
ThreadLocal类的常用方法有如下几个:
ThreadLocal(),创建一个线程本地变量
get(),返回此线程局部变量的当前线程副本中的值
initialValue(),返回此线程局部变量的当前线程的"初始值"
set(T value),将此线程局部变量的当前线程副本中的值设置为value
ThreadLocal.ThreadLocalMap.Entry中的key是弱引用的,但是value是基于强引用的,当某个ThreadLocal对象不存在强引用时,且GC后,key会被回收,但是value还存强引用时,因此就会出现内存的泄露情况。目前最好的解决方式就是在使用完ThreadLocal及时调remove方法

6、线程安全

线程安全就是多线程访问时采用了加锁机制,当一个线程访问该类的某个数据时进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染
线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
Java中典型的非线程安全和线程安全的集合类对比如下列表。在需要考虑线程安全问题的情况下,使用线程安全的集合类实现较简单,但会影响性能,所以通常大多数的集合类是非线程安全的

非线程安全线程安全
ArrayListVector
HashMapHashTable
StringBuilderStringBuffer

7、线程池

主要作用

1)降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗
2)提高响应速度,当任务到达时,任务可以不需要等到线程创建就能立即执行
3)提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性。使用线程池可以进行统一分配、调优和监控

线程池的创建

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,TimeUnit unit,   BlockingQueue workQueue,   ThreadFactory threadFactory,   RejectedExecutionHandler handler)

使用构造方法ThreadPoolExecutor(),各个参数的含义概述如下
1)corePoolSize: 线程池核心线程数最大值
2)maximumPoolSize: 线程池最大线程数大小
3)keepAliveTime: 线程池中非核心线程空闲的存活时间大小
4)unit: 线程空闲存活时间单位
5)workQueue: 存放任务的阻塞队列
6)threadFactory: 用于设置创建线程的工厂,可以给创建的线程设置有意义的名字,可方便排查问题
7)handler: 线城池的饱和策略事件,主要有四种类型

调用execute方法的执行流程如下,
在这里插入图片描述

1)提交一个任务,线程池里存活的核心线程数小于线程数corePoolSize时,线程池会创建一个核心线程去处理提交的任务。
2)如果线程池核心线程数已满,即线程数已经等于corePoolSize,一个新提交的任务,会被放进任务队列workQueue排队等待执行。
3)当线程池里面存活的线程数已经等于corePoolSize了,并且任务队列workQueue也满,判断线程数是否达到maximumPoolSize,即最大线程数是否已满,如果没到达,创建一个非核心线程执行提交的任务。
4)如果当前的线程数达到了maximumPoolSize,还有新的任务过来的话,直接采用拒绝策略处理。

几种常用的线程池

1)newFixedThreadPool (固定数目线程的线程池)
适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务
2)newCachedThreadPool(可缓存线程的线程池)
适用于并发执行大量短期的小任务
3)newSingleThreadExecutor(单线程的线程池)
适用于串行执行任务的场景,一个任务一个任务地执行
4)newScheduledThreadPool(定时及周期执行的线程池)
适用于周期性执行任务的场景,需要限制线程数量的场景

8、查看线程运行情况的命令

  1. 查看指定进程pid,以京东为例
    执行 adb shell “ps | grep jingdong”,结果如下
    u0_a415 3050 554 2707208 339720 0 0 S com.jingdong.app.mall
    u0_a415 3739 554 2274060 159168 0 0 S com.jingdong.app.mall:manto0
    u0_a415 4167 554 1895632 82336 0 0 S com.jingdong.app.mall:jdpush
    由此可知,京东app启动了三个进程,其中主进程的pid是3050
  2. 查看京东app主进程的线程运行情况
    执行 adb shell “ps -T -p 3050”,结果如下
    USER PID TID PPID VSZ RSS WCHAN ADDR S CMD
    u0_a415 3050 3050 554 2704168 339868 0 0 S ngdong.app.mall
    u0_a415 3050 3066 554 2704168 339868 0 0 S Jit thread pool
    u0_a415 3050 3067 554 2704168 339868 0 0 S Signal Catcher
    u0_a415 3050 3068 554 2704168 339868 0 0 S ADB-JDWP Connec
    u0_a415 3050 3069 554 2704168 339868 0 0 S ReferenceQueueD
    u0_a415 3050 3070 554 2704168 339868 0 0 S FinalizerDaemon

Android中的线程

1、关于应用主线程

启动应用时,系统会为该应用创建一个称为“main”(主线程)的执行线程。此线程非常重要,因为其负责将事件分派给相应的界面微件,其中包括绘图事件。此外,应用与 Android 界面工具包组件(来自 android.widget 和 android.view 软件包的组件)也几乎都在该线程中进行交互。因此,主线程有时也称为界面线程
系统不会为每个组件实例创建单独的线程。在同一进程中运行的所有组件均在界面线程中进行实例化,并且对每个组件的系统调用均由该线程进行分派。因此,响应系统回调的方法(例如,报告用户操作的 onKeyDown() 或生命周期回调方法)始终在进程的界面线程中运行

在这里插入图片描述

在写android界面相关代码时,需切记如下两个要点
1)不要阻塞UI线程
2)不要在UI线程之外访问Android UI工具包

2、工作线程

要保证应用界面的响应能力,关键是不能阻塞界面线程。如果执行的操作不能即时完成,则应确保它们在单独的线程(“后台”或“工作”线程)中运行
为解决此问题,Android 提供了几种途径,以便您从其他线程访问界面线程
1)Activity.runOnUiThread(Runnable)
2)View.post(Runnable)
3)View.postDelayed(Runnable, long)
4)如要通过工作线程处理更复杂的交互,可以考虑使用AsyncTask、Handler&Thread、HandlerThread、IntentService等机制实现
5) AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。从实现上来说,AsyncTask封装了Thread和Handler,通过AsyncTask我们更加方便的执行后台任务以及在主线程中访问UI,但是AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池
6) HandlerThread继承了Thread,它是一种可以使用Handler的Thread。它的实现也很简单,就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,这样就允许在HandlerThread中创建Handler了
7) IntentService适合于执行由UI触发的后台Service任务,并可以把后台任务执行的情况通过一定的机制反馈给UI

极速版App中的线程

1、主工程中使用的线程

自定义的工作线程类NonUIThread,主要职责是按序初始化定位、加载多dex、注册推动服务等

1)在App->MyApplication类的attach阶段创建并启动的
2)通过继承Thread类的方式实现
3)通过synchronized关键字同步方法来实现单例线程类
4)默认的线程优先级是NORM_PRIORITY
5)使用obj.wait()和notify()的方式改变线程状态
6)使用setPriority(Thread.MIN_PRIORITY)方法设置线程优先级


public class MyApplication extends Application {

    private ProcessInitLifeCycle processInit;


    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        try {
            NonUIThread.getInstance().start();
        } catch (IllegalThreadStateException e) {
            // throws IllegalThreadStateException - if this thread has already started.
            // I still cannot figure out the root cause yet, just catch it
            e.printStackTrace();
        }
        loadMultiDex(base);
        //所有的需要启动初始化需要做的事情在这里执行
        initAppLike(base);
    }
 }

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

    private Handler mHandler;
    private final Object oLock = new Object();
    private final Object oIdleLock = new Object();
    private int previousPriority = Thread.NORM_PRIORITY;
    private final AtomicBoolean started = new AtomicBoolean(false);

    private NonUIThread(){

    }

    private static volatile NonUIThread instance;
    public static synchronized NonUIThread getInstance(){
        if(null == instance){
            instance = new NonUIThread();
        }
        return instance;
    }

    @Override
    public void run() {
        if (started.getAndSet(true)) return;
        Looper.prepare();

        synchronized (oLock) {
            mHandler = new Handler();
            oLock.notify();
        }

        Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle() {
                synchronized (oIdleLock){
                    oIdleLock.notify();
                }
                return true;
            }
        });

        Looper.loop();
    }

    public void postRunnable(Runnable runnable){
        synchronized (oLock) {
            if(null == mHandler){
                try {
                    oLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            mHandler.post(runnable);
        }
    }

    public void waitForIdle(){
        synchronized (oIdleLock){
            try {
                oIdleLock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

广告弹窗业务核心类ADActvity,主要职责是负责广告弹窗的处理逻辑

1)使用AsyncTask来实现线程交互
2)调用execute()方法实际上是串行执行任务。使用executeOnExecutor(Executor)可以并行执行,但是这个api有版本限制
3)doInBackground(Params…)后台执行,比较耗时的操作都可以放在这里,注意这里不能直接操作UI
4)当前页面退出时,一般需要调用cancel()方法取消当前task,否则存在异常风险
5)AsyncTask适用于后台操作只有几秒的短时操作,否则存在内存泄露风险

public class ADActivity extends BaseActivity implements View.OnClickListener {
    private String homePage;
    private static final String AD_SHOW_TIME = "ad_show_time";
    private ADEntry adEntry;
    private int timerCount = 5;
    private TextView ignoreButton;
    private JumpEntity jumpEntity;
    private boolean isJumped;
    private Handler showTimeHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
        		...
        }
    };
    private long startTime;
    private AsyncTask<String, Integer, Bitmap> asyncTask = new AdDisplayTask();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        startTime = SystemClock.elapsedRealtime();
        statusBarTransparentEnable = true;
        super.onCreate(savedInstanceState);
        ...
        setContentView(R.layout.activity_a_d);
        ...
        asyncTask.execute(adPath);
    }

    @Override
    protected void onResume() {
        super.onResume();
        isJumped = false;
        if (showTimeHandler != null) {
            showTimeHandler.sendEmptyMessageDelayed(0, 1000);
        } else {
            onIgnore(null);
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        finish();
    }

    @Override
    public void finish() {
        if (showTimeHandler != null) {
            if (showTimeHandler.hasMessages(0))
                showTimeHandler.removeMessages(0);
            showTimeHandler = null;
        }
        if (asyncTask != null && !asyncTask.isCancelled()) {
            asyncTask.cancel(false);
        }
        super.finish();
    }

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

    @Override
    public void onClick(View v) {
        onJump(v);
    }

     class AdDisplayTask extends AsyncTask<String, Integer, Bitmap> {

        @Override
        protected Bitmap doInBackground(String... strings) {
            try {
                if (strings == null || strings.length == 0) return null;
                String path = strings[0];
                if (TextUtils.isEmpty(path) || !new File(path).exists()) return null;
                return BitmapFactory.decodeFile(path);
            } catch (Throwable e) {
                return null;
            }
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
           ...
        }
    }
}

日志上报核心类LogReporter,主要职责是上报各页面的简单日志信息

1)使用了newFixedThreadPool线程池,长度设定为5
2)默认的线程优先级是MIN_PRIORITY,不抢占其他线程的资源
3)创建ThreadFactory创建新线程,维护线程工厂
4)有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象

public class LogReporter extends AbsLogReporter {
	...
    private ExecutorService es;

    LogReporter(String strategyParam) {
        es = Executors.newFixedThreadPool(5, new ThreadFactory(){
            @Override
            public Thread newThread(@NonNull Runnable r) {
                Thread thread = new Thread(r);
                thread.setPriority(Thread.MIN_PRIORITY);
                return thread;
            }
        });
        if (!TextUtils.isEmpty(strategyParam)) {
            param = JDJSON.parseObject(strategyParam, LogStrategyParam.class);
            if (null != param) {
                param.parseParams();
                JDActivityLifeCycleCallBack callBack = JDActivityLifeCycleCallBack.getInstance();
                callBack.setParam(param);
                JdSdk.getInstance().getApplication().registerActivityLifecycleCallbacks(callBack);
            }
        }
        process = ProcessUtil.getProcessName(JdSdk.getInstance().getApplication());
    }

    ...
    @Override
    public void report(final HashMap<String, String> data) {
        es.execute(new Runnable() {
            @Override
            public void run() {
                ArrayList<HashMap<String,String>> list = new ArrayList<>();
                list.add(data);
                boolean r = PerformanceReporter.reportData(list);
            }
        });
    }

    /**
     * report very simple msg for INNER, do not open for other package
     *
     * @param msg
     */
    void reportSimpleMessage(String msg) {
        HashMap<String, String> map = getAdditionalData();
        map.put("exceptionType", "");
        map.put("className", "");
        map.put("msg", msg);
        map.put("methodStack", "");
        map.put("occurTime", String.format("%.6f", System.currentTimeMillis() / 1000.0f));
        map.put("logLevel", "INNER");//private log level
        map.put("logTag", "ALC");//ActivityLifeCycle
        report(map);
    }

}

2、三方框架网络库中使用的线程

3、业务模块首页中使用的线程

1)在HomeUtil中封装了切换线程的处理逻辑
2)首页模块的各页面更新UI统一通过runOnUiThread()方法分发
3)首页模块只有PageInfoUtils类的collectCrashData()方法中直接new Thread(),但这个函数没有被调用

public class HomeUtil {

    public static Handler sHandler = new Handler(Looper.getMainLooper());
    ...
    /**
     * @return 是否主线程
     */
    public static boolean isMainThread() {
        return Looper.myLooper() == Looper.getMainLooper();
    }

    /**
     * @return 是否子线程
     */
    public static boolean isSubThread() {
        return Looper.myLooper() != Looper.getMainLooper();
    }

    /**
     * 判断是否主线程,主线程直接执行,否则post至主线程。
     */
    public static void runOnUiThread(BaseRun runnable) {
        if (isMainThread()) {
            runnable.run();
        } else {
            sHandler.post(runnable);
        }
    }

    /**
     * 不判断是否主线程,直接post至主线程。
     */
    public static void postOnUiThread(BaseRun runnable) {
        sHandler.post(runnable);
    }

    public static void runOnUiThread(BaseRun runnable, long delayMillis) {
        sHandler.postDelayed(runnable, delayMillis);
    }
    ...
 }

4、业务模块任务中心使用的线程

1)TaskFloatActivity类中的读取通讯录方法readContactList()中,通过new Thread()开启了线程
2)在线程函数中弱引用了当前的TaskFloatActivity类对象,当系统执行gc的时候该对象会被回收,读取联系的人方法buildContactsDataWithRegion()是否会抛异常待验证???
3)如果当前页面TaskFloatActivity退出了,这里通过isDestroy变量控制线程的退出,以保证后面的操作不再执行
4)建议在通讯录sdk里做修改,当TaskFloatActivity退出后,通过全局变量的方式让读取联系人的方法buildContactsDataWithRegion()快速执行完

public class TaskFloatActivity extends MvpBaseActivity<TaskCenterPresenter, BaseNavigator> implements IMissionView, PullToRefreshBase.OnRefreshListener<RecyclerView>, View.OnClickListener, MissionActiveItemView.ItemClickCallback, INewUserInterface {
	...
    /**
     * 获取通讯录明细
     */
    private void readContactList() {
        if (isDestroy || isReading) {
            return;
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    isReading = true;
                    //获取通讯录list
                    WeakReference<Activity> contextWeakReference = new WeakReference<Activity>(getThisActivity());
                    JSONArray phoneList = ContactUtils.getPhoneList(contextWeakReference.get());
                    if (isDestroy) {
                        isReading = false;
                        return;
                    }
                    if (null != phoneList && phoneList.length() > 0) {
                        if (Build.VERSION.SDK_INT < 23) {
                            //小于6.0的设备,读到证明有权限
                            updateContactList(3, phoneList);
                        } else {
                            //上传
                            ContactUtils.upload(MissionUtils.CONTACT_ACT_ID, phoneList);
                        }
                    } else {
                        //通讯录为空,小于6.0的设备
                        if (Build.VERSION.SDK_INT < 23) {
                            post(new Runnable() {
                                @Override
                                public void run() {
                                    showErrorDialog();
                                }
                            });
                        }
                    }
                    isReading = false;
                } catch (Exception e) {
                    isReading = false;
                    e.printStackTrace();
                }
            }
        }).start();
    }
    ...
 }

5、线程使用注意事项

1)更新UI务必在主线程,极速版使用的网络框架回调是在子线程里执行的,需要切到主线程更新UI
2)尽量减少使用new Thread()方式,使用时充分考虑线程安全问题,页面退出时要确保线程快速执行完(满足需求的前提下)
3)如果页面退出后,线程仍需要继续执行完,建议使用Service来实现
4)异步任务比较多时,应该使用线程池
5)虽然使用多线程可以提高程序的并发量,但是我们需要特别注意因为引入多线程而可能伴随而来的内存问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值