Java与Android线程学习总结

目录

线程、进程等的区别

开启线程的方式

线程生命周期

线程同步

并发编程三个问题

Java同步的方法

Synchronized

总结比较

多线程操作事项

死锁

线程池

ThreadLocal

AsyncTask

注意事项

如何防止线程的内存泄漏?


线程、进程等的区别

进程是操作系统资源分配的基本单位(独立的内存),而线程是任务调度和执行的基本单位。

为什么要有线程,而不是仅仅用进程:进程只能在一个时间干一件事情,如果堵塞了,整个进程就会挂起。线程有助于并发,提高利用率。

线程模型:使用内核线程实现、使用用户线程实现和使用用户线程加轻量级进程混合实现。参考:https://www.cnblogs.com/wade-luffy/p/6051384.html

Android线程有没有上限? Android系统对应用程序资源的限制是以进程为单位的,一般一个应用开不到最大线程就会被内存限制死。

开启线程的方式

Thread, Runnable, 使用Callable和Future创建线程。 Runnable是个接口,Thread 实现了Runnable。Callable能返回执行结果。

线程生命周期

  1. run()和start()方法区别:start才是真正启动了线程;run只是Thread的一个普通方法,若直接调用run是在本线程中。
  2. 在Java中wait和seelp方法的不同;sleep()方法是属于Thread类中的。而wait()方法则是属于Object类中。调用sleep后不会释放锁,wait会释放。
  3. 谈谈wait/notify关键字的理解:在生产者调用notify()后,消费者并没有立即被唤醒,而是等到生产者退出同步块后才唤醒执行。
  4. 什么导致线程阻塞? sleep,wait, yield,join, suspend。
  5. 线程如何关闭? 设置标志位,调用interrupt方法。

线程同步

并发编程三个问题

原子性问题,可见性问题,有序性问题。

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。有序性即程序执行的顺序按照代码的先后顺序执行。CPU为了优化执行效率可能会讲指令重排。

Java同步的方法

synchronized,volatile(对变量的操作会立即刷到主存,所以能保证可见性。并且能保证一定的有序性),重入锁(Lock),wait与notify(是Object的方法)ThreadLocal,BlockingQueue,AtomicInteger 原子变量。

Synchronized

Synchronized用法:锁对象,方法(包括静态方法),代码块。 一定要注意synchronized 锁的是啥?是否只有一个实例 。synchronized方法 锁的是方法所属对象本身. 同一个锁某个时刻只能被一个执行线程所获取。即同一个类里面两个synchronized方法,不能同时被两个线程访问。

是可重入的:在执行对象中所有同步方法不用再次获得锁。好处:一定程度上避免了死锁。

synchronized的原理:基于进入和退出管程(Monitor)对象实现。管程是一种进程同步互斥工具,类似信号量与PV操作等。 参考https://blog.csdn.net/javazejian/article/details/72828483

谈谈对Synchronized关键字,类锁,方法锁,重入锁的理解。

锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁,但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级。

Lock的实现类常用的有ReentrantLock、ReentrantReadWriteLock

Lock原理,以ReentrantLock为例。ReentrantLock的内部实现:依托CAS(比较与交换,原子操作的一种。在写之前应该比较该值有没有发生变化)原理,通过AbstractQueuedSynchronizer 类实现, CLH队列,虚拟队列,不存在队列实例,仅存在节点前后的关系。

总结比较

ReentrantLock 、synchronized和volatile比较:syncrhronized 是关键字由JVM实现,使用简单(1.6优化引入了偏向锁、轻量级锁,后性能和ReentrantLock性能差不多),ReentrantLock 是一个类,由JDK实现(可理解为由用户实现,原理是避免线程进入内核的阻塞状态) 在竞争激烈时效率更高。其三个有点(1.可以指定公平锁和非公平锁,synchronized只能是非公平锁;2.提供Condition来实现分组唤醒,synchronized要么随机唤醒一个要么唤醒全部线程,3.通过lock.lockInterruptibly实现中断等待锁的线程。)

多线程操作事项

  1. 两个进程同时要求写或者读,能不能实现?如何防止进程的同步? ——能同时读,不能同时写。信号量PV操作。
  2. 多线程操作List:把list分为几段,用多线程处理。
  3. 如何控制某个方法允许并发访问线程的个数:利用Semaphore信号量
  4. static synchronized 方法的多线程访问和作用;
  5. NIO的理解: NewI/O,能提升读写性能。基于块Block,以块为基本单位处理数据。有Buffer和Channel通道,提供了基于Selector的异步网络I/O。通道表示缓冲数据的源头或者目的地,它用于向缓冲读取或者写入数据,是访问缓冲的接口。
    1. 使用对文件加锁的方式做到线程安全;FileChannel。读文件时用共享锁,写文件时用独占锁。

死锁

死锁的四个必要条件:互斥、请求与保持、不可剥夺、循环等待。

怎么避免死锁?主要是破坏后面三种情况。1.不允许进程在已获得某种资源的情况下,申请其他资源。2.允许对资源进行抢夺。3.将系统中的所有资源统一编号,进程可在任何时刻提出资源申请,但所有申请必须按照资源的编号顺序(升序)提出。

线程池

线程池

ThreadLocal

参考:ThreadLocal详解

提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量。只有当线程第一次调用ThreadLocal的set或者get方法的时候才会创建他们。通过线程里面的ThreadLocalMap是使用弱引用存值。用完后要调用remove方法,否则容易造成内存泄漏。

同一个ThreadLocal变量在父线程中被设置值后,在子线程中是获取不到的。(threadLocals中为当前调用线程对应的本地变量,所以二者自然是不能共享的)

使用场景:ThreadLocal的应用场景

场景1:

ThreadLocal 用作保存每个线程独享的对象,为每个线程都创建一个副本,这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本,确保了线程安全。

场景2:

ThreadLocal 用作每个线程内需要独立保存信息,以便供其他方法更方便地获取该信息的场景。每个线程获取到的信息可能都是不一样的,前面执行的方法保存了信息后,后续方法可以通过ThreadLocal 直接获取到,避免了传参,类似于全局变量的概念。

AsyncTask

AsyncTask原理及不足:AsyncTask是对Handler与线程池的封装,是一个抽象类,我们在使用时需要定义一个它的派生类并重写相关方法。

onPreExecute() //此方法会在后台任务执行前被调用,用于进行一些准备工作
doInBackground(Params... params) //此方法中定义要执行的后台任务,在这个方法中可以调用publishProgress来更新任务进度(publishProgress内部会调用onProgressUpdate方法)
onProgressUpdate(Progress... values) //由publishProgress内部调用,表示任务进度更新
onPostExecute(Result result) //后台任务执行完毕后,此方法会被调用,参数即为后台任务的返回结果
onCancelled() //此方法会在后台任务被取消时被调用
除了doInBackground方法由AsyncTask内部线程池执行外,其余方法均在主线程中执行。

局限性如下:
1. 在Android 4.1版本之前,AsyncTask类必须在主线程中加载,这意味着对AsyncTask类的第一次访问必须发生在主线程中;在Android 4.1以及以上版本则不存在这一限制,因为ActivityThread(代表了主线程)的main方法中会自动加载AsyncTask
2. AsyncTask对象必须在主线程中创建
3. AsyncTask对象的execute方法必须在主线程中调用
4. 一个AsyncTask对象只能调用一次execute方法

AsyncTask对象必须在主线程中创建:sHandler是一个静态的Handler对象。创建Handler对象时需要当前线程的Looper,所以我们为了以后能够通过sHandler将执行环境从后台线程切换到主线程(即在主线程中执行handleMessage方法),必须使用主线程的Looper,因此必须在主线程中创建sHandler。

如何取消AsyncTask?     调用cancel方法,

为了保证任务尽快被取消,应该在doInBackground中周期性去检测iscancelled方法;

优缺点参考:AsyncTask的优缺点

注意事项

如何防止线程的内存泄漏?

将Runnable做成static的方式,防止Runnable应用Activity对象;

猜你喜欢

转载自blog.csdn.net/lanmengfenghe/article/details/111247109
今日推荐