笔记(十二)——线程与进程

——》个人平时笔记,看到的同学欢迎指正错误,文中多处摘录于各大博主精华、书籍
1、Java的线程生命周期有六种状态:

New(初始化状态)
Runnable(可运行/运行状态)
Blocked(阻塞状态)
Waiting(无时间限制的等待状态)
Timed_Waiting(有时间限制的等待状态)
Terminated(终止状态)

2、在开发中我们经常会在UI线程中开启子线程去执行耗时操作然后使用Handler机制通信,这里是不建议这么操作的,操作不当容易造成内存溢出或NullPointException空指针;例如我们还没有执行完耗时操”作就离开了当前页面,而没有结束子线程的行为,而子线程又持有外部UI的Handler引用。所以当有耗时操作时提倡异步任务AsyncTask执行(注意AsyncTask在onDestory中要cancle关闭,否则也会内存泄漏)使用或采用Handler静态内部类+Activity弱引用的方式。

线程同步锁(同步锁机制synchronized):如A线程要请求某个资源,但是此资源加了同步锁又正在被B线程使用着,因为同步机制存在,A线程请求不到该资源,怎么办,A线程只能继续等待下去。注意没有线程同步的说法,代码执行的任务可以说是同步的自上而下运行。

线程异步:A线程要请求某个资源,但是此资源正在被B线程使用中,因为现在没有加同步锁,所以A线程能请求的到该资源,故A线程无需等待。异步也可以理解为在主线程中开启一个子线程,而让子线程执行一部分任务,主线程继续执行下去而不必等待子线程执行完;开启一个子线程就是一个异步线程操作。

volatile:与锁相比,volatile变量是一种非常简单但同时又非常脆弱的同步机制,它在某些情况下将提供优于锁的性能和伸缩性。如果严格遵循volatile的使用条件,使变量真正独立于其他变量和自己以前的值,在某些情况下可以使用volatile代替synchronized来简化代码。然而,使用volatile的代码往往比使用锁的代码更加容易出错。

synchronized加到static静态方法上是对Class类上锁,而synchronized加到非static方法上是给对对象上锁。Class锁可以对类的所有对象实例起作用。

>1、 什么时候必须同步?什么叫同步?如何同步?

要跨线程维护正确的可见性,在多个线程之间没有用final修饰的共享变量,就必须使用synchronized(或volatile)以确保一个线程可以看见另一个线程做的更改。

小结:为了防止多个线程并发时对同一数据变量做出修改,所以需要同步的地方需要加锁,否则会造成数据不一致(就是所谓的:线程安全。如java集合框架中Hashtable和Vector是线程安全的。我们的大部分程序都不是线程安全的,因为没有进行同步,但是我们没有必要同步,因为大部分情况根本没有多线程环境),所以这里才会使用线程锁(synchronized)。

>2、进程是具有一定独立功能的程序,是关于某个数据集合上的一次运行活动。进程是系统进行资源分配和调度的一个独立单位,通常情况下一个app应用就是一个进程,但是开发者可以在一个应用中创建多个进程。

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程的执行是抢占式的,所以CPU执行哪个线程具有不确定性的。

进程是cpu资源分配的最小单位,线程是cpu调度的最小单位。以前进程既是资源分配也是调度的最小单位,后来为了更合理的使用cpu(实际上是cpu性能越来越好),才将资源分配和调度分开,就有了线程。

(1)线程是进程的一部分

(2)CPU调度的是线程

(3)系统为进程分配资源,不对线程分配资源

总结起来,使用多线程编程具有如下几个优点:
01. 进程之间不能共享内存,但线程之间共享内存非常容易,同一进程内的线程共享资源。
02. 系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高。
03. Java语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了Java的多线程编程。
04.线程中Thread.start()是异步是会生成子线程的;Thread.run()是同步依然在主线程,且需要依次执行。
05.少用Thread.sleep(),它是一个阻塞方法,容易抛出InterruptedException;导致程序中断,而中断可能导致应用程序丧失及时取消活动或关闭的能力;建议使用SystemClock.sleep(),它不考虑中断异常,将可能发生的中断推迟到下一次中断来临。

3、并发与并行是基于硬件而言的。

并发,指的是多个事情,在同一时间段内同时发生了。但在一个cpu中同一时刻只能有一条指令执行,所以微观上多个进程指令被快速轮流执行,而在宏观上多个进程指令是在同时执行的。

并行,指的是多个事情,在同一时间点上同时发生了。指在同一时刻,有多条指令在多个处理器上真正的同时执行。

1.并发的多个任务之间是互相抢占资源,多条线程轮流(轮换)执行在一个CPU中(单核CPU)
2.并行是多个任务之间不互相抢占资源,多个线程同时执行(多核CPU),微观上是同时的
3.串行是指一个一个的执行,处理完一个才能处理下一个,不抢占不轮换资源这是与并发的大区别

并行只有在多CPU的情况中才会发生。否则,看似同时发生的事情,其实都是并发执行的。

image

4、除了Thread线程本身以外还有三种线程的封装类AsyncTask、IntentService、HandlerThread。

  • AsyncTask的底层用到了线程池,IntentService和HandlerThread它们的底层则直接使用了线程。AsyncTask封装了线程池和Handler,它主要是为了方便开发者在子线程中更新UI,一个AsyncTask对象只能执行一次,即只能调用一次execute方法且只能在主线程中调用运行。在Android3.0及以上版本,一个进程中所有的AsyncTask全部在同一个串行的线程池Executor中排队串行执行,但是Android3.0以前是并行执行的;为了让程序在Android3.0及以上版本可以并行执行,系统给AsyncTask提供了executeOnExecutor方法代替execute方法启动而实现并行。

构成:AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler),其中线程池SerialExecutor用于任务的排队,而线程池THREAD_POOL_EXECUTOR用于真正地执行任务,InternalHandler用于将执行环境从线程池切换到主线程。

  • HandlerThread是一种继承Thread具有消息循环的线程,在它的内部可以使用Handler,因为内部已经初始化和调用了Looper。
  • IntentService是一个服务,是一个特殊的Service抽象类。系统对其进行了封装使其可以更方便地执行后台任务,IntentService内部采用HandlerThread和Handler来执行任务,并且它也是按顺序执行后台任务的。当有多个后台任务同时存在时,这些后台任务会按照外界发起的顺序排队执行。当任务执行完毕后IntentService会自动退出。IntentService由于是一个Service,优先级高于普通线程更不易被系统杀死。

5、除了主线程以外的线程都是子线程。主线程的作用是运行四大组件以及处理它们和用户的交互,因此主线程必须时刻保持较高的响应速度,所以不能在主线程执行耗时操作,以免造成界面卡顿,这是系统层规定的;而子线程的作用则就是执行耗时任务(网络请求、I/O操作等),为主线程分担压力,并且在Android 3.0开始系统已经要求网络访问必须在子线程中进行,否则抛出NetworkOnMainThreadException异常。

6、线程池:
>1.线程池的优点可以概括为以下三点:

(1)重用线程池中的线程,当没有空闲的核心线程且任务栈已满时才创建新的非核心线程,避免因为线程的创建和销毁所带来的性能开销。
(2)能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
(3)能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。

>2.ThreadPoolExecutor执行任务时大致遵循如下规则:

(1)如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
(2)如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务才会被插入到任务队列中排队等待执行。 BlockingQueue<Runnable> workQueue;任务队列存放任务等待核心线程执行,并非存放线程。
(3)如果在步骤2中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动创建一个新的非核心线程来执行任务。
(4)如果步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法来通知调 用者。
(5)线程池的启动new ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory).execute(new Runnable());

>3.分类:各种线程池都是都直接或间接地通过配置ThreadPoolExecutor参数来实现自己的功能特性。常见的分为了4种线程池FixedThreadPool、CachedThreadPool、ScheduledThreadPoll 、SingleThreadExecutor,本质都是对ThreadPoolExecutor的封装只是ThreadPoolExecutor创建时传递的实参不同。
(1)、FixedThreadPool 正规线。我的理解这是一个有指定的线程数的线程池,有核心的线程,里面有固定的线程数量,响应的速度快。正规的并发线程,多用于服务器。固定的线程数由系统资源设置。
(2)、CaCheThreadPool 缓存线程池。只有非核心线程,最大线程数很大(Int.Max(values)),它会为每一个任务添加一个新的线程,这边有一个超时机制,当空闲的线程超过60s内没有用到的话,就会被回收。缺点就是没有考虑到系统的实际内存大小。
(3)、SingleThreadPoll 单线程线程池。看这个名字就知道这个家伙是只有一个核心线程,就是一个孤家寡人,通过指定的顺序将任务一个个丢到线程,都乖乖的排队等待执行,不处理并发的操作,不会被回收。确定就是一个人干活效率慢。
(4)、ScheduledThreadPoll 这个线程池就厉害了,是唯一 一个有延迟执行和周期重复执行的线程池。它的核心线程池固定,非核心线程的数量没有限制,但是闲置时会立即会被回收。

7、为什么在onCreate()中的子线程可以操作UI?首先这是一种极端的特殊情况。参考鸿洋大神Android中子线程真的不能更新UI吗?
View的绘制过程就是从ViewRootImpl的performTraversals方法开始的,而ViewRootImpl的创建在onResume方法回调之后,而我们在onCreate方法中创建了子线程并访问UI,在那个时刻,ViewRootImpl是没有创建的,无法检测当前线程是否是UI线程,所以程序没有崩溃且一样能跑起来,而如果让线程休眠了200毫秒后,程序就崩了。这是因为200毫秒后ViewRootImpl已经创建了,可以执行checkThread方法检查当前线程是否是主线程在操作UI,如果不是就抛出错误信息。
8、Java之CountDownLatch ---控制线程执行顺序
9、线程的生命周期
创建 New
就绪 Runnable
运行 Running
阻塞 Blocked
死亡 Dead
10、start与run的区别:线程的任务处理逻辑可以在Tread类的run实例方法中直接实现或通过该方法进行调用而直接运行Thread内的run方法,因此run()相当于线程的任务处理逻辑的入口方法,它由Java虚拟机在运行相应线程时直接调用,而不是由应用代码进行调用,多个thread.run()顺序执行,也就是并没有创建新的子线程。
而start()的作用是启动相应的线程进入就绪状态。启动一个线程实际是请求Java虚拟机运行相应的线程,而这个线程何时能够运行是由线程调度器决定的。start()调用结束并不表示相应线程已经开始运行,这个线程可能稍后运行,也可能永远也不会运行。
11、sleep()和wait()区别?

(1)
sleep()是Thread类的静态方法,会使线程暂停一段时间,时间到了就会自动苏醒;
wait()是Object类的方法,使线程暂停执行,需要被通知notify()才能醒
(2)
sleep()不涉及线程间的通信,调用sleep()不会释放锁(所以容易产生死锁);
wait()用于线程间的通信,调用wait()后线程会释放它所占用的锁
(3)
sleep()可放在任何地方使用;
wait()放在synchronized方法或语句块中使用
(4)
sleep()必须捕获异常(InterruptedException)
wait()不需要捕获异常

12、synchronized和Lock区别?

(1)存在层次:
synchronized是关键字,在JVM层面上;
Lock是接口,通过代码实现
(2)锁的释放:
synchronized会自动释放锁
Lock必须是开发人员在finally中释放锁,否则会死锁
(3)锁的获取:
synchronized是A线程获得了锁,B线程就必须等待。若A阻塞,B也会一直等待
Lock有多种方式获取锁,可以不用一直等待
(4)性能
synchronized在线程竞争不激烈时效率高,竞争激烈时效率下降很快
Lock效率较为稳定

13、线程间通信的几种方式

1.使用Handler
2.AsyncTask:其内部也是使用了handler和维持了一个线程池
3.View.postDelay(Runnable r)与View.post(Runnable r):Runnable 运行与View绘制显示完成后
4.runOnUiThread(Runnable r)方法:子线程持有activity时可以切换到UI线程
5.使用Broadcast子线程中发送广播,主线程中接收广播并更新UI
6.使用EventBus与RxBus三方库
14、Android dvm的进程和Linux的进程, 应用程序的进程是否为同一个概念以及Dalvik和Java运行环境的区别
解答:每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik实例。而每一个DVM都是在Linux 中的一个进程,所以说可以认为是同一个概念。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容