1. 线程相关概念
进程:一个操作系统中可以同时运行多个任务(程序),每个运行的任务(程序)被称为一个进程。
线程:一个程序同时可能运行多个任务(顺序执行流),那么每个任务(顺序执行流)就叫做一个线程。即在进程内部。
并发:线程是并发运行的。操作系统将时间化分为若干个片段(时间片),尽可能的均匀分配给每一个任务,被分配时间片后,任务就有机会被cpu所执行。微观上看,每个任务都是走走停停的。但随着cpu高效的运行,宏观上看所有任务都在运行。这种都运行的现象称之为并发,但不是绝对意义上的“同时发生”。
线程调度:线程调度机制会将所有并发任务做统一的调度工作,划分时间片(可以被cup执行的时间)给每一个任务,时间片尽可能的均匀,但做不到绝对均匀。同样,被分配时间片后,该任务被cpu执行,但调度的过程中不能保证所有任务都是平均的获取时间片的次数。只能做到尽可能平均。这两个都是程序不可控的。
线程的启动和停止:void start():想并发操作不要直接调用run方法!而是调用线程的start()方法启动线程!void stop():不要使用stop()方法来停止线程的运行,这是不安全的操作,想让线程停止,应该通过run方法的执行完毕来进行自然的结束。
2. 线程的创建方式
1)继承自Thread,然后重写run方法:run方法中应该定义我们需要并发执行的任务逻辑代码。
案例29:
2)实现Runnalbe接口。因为有了这样的设计,才有了线程池。
Runnable接口:用于定义线程要执行的任务逻辑。我们定一个类实现Runnable接口,这时我们必须重写run方法,在其中定义我们要执行的逻辑。之后将Runnable交给线程去执行。从而实现了线程与其执行的任务分离开。将任务分别交给不同的线程并发处理,可以使用线程的重载构造方法:Thread(Runnable runnable)。解藕:线程与线程体解藕,即打断依赖关系。
案例30:
3)线程的创建方式三:使用匿名内部类方式创建线程
案例31:
3. 线程的生命周期
线程的生命周期有5种状态:创建(new)、就绪(runnable)、运行(running)、阻塞(block)、死亡(dead)。
4. 线程的常用方法
① static void sleep(times)方法:让当前线程主动进入Block阻塞状态,并在time毫秒后回到Runnalbe状态。 注意事项:使用Thread.sleep()方法阻塞线程时,强制让我们必须捕获“中断异常”。
案例32:
② void interrupt()方法:打断/唤醒线程。一个线程可以提前唤醒另外一个sleep Block的线程。
注意事项:方法中定义的类叫局部内部类:局部内部类中,若想引用当前方法的其他局部变量,那么该变量必须是final的。
案例33:
③ static void yield():当前线程让出处理器(离开Running状态)即放弃当前时间片,主动进入Runnable状态等待。
④ final void setPriority(int):设置线程优先级;优先级越高的线程,理论上获取cpu的次数就越多。但理想与现实是有差距的……设置线程优先级一定要在线程启动前设置!
案例34:
5. wait和notify方法
这两个方法不是在线程Thread中定义的方法,这两个方法定义在Object中。两个方法的作用是用于协调线程工作的。
(1) 等待机制与锁机制密切关联:wait/notify方法必须与synchronized同时使用,谁调用wait或otify方法,就锁谁!
(2) wait()方法:当条将不满足时,则等待。当条件满足时,等待该条件的线程将被唤醒。如:浏览器显示一个图片,displayThread要想显示图片,则必须等代下载线程downloadThread将该图片下载完毕。如果图片没有下杂完成,则dialpayThread可以暂停。当downloadThread下载完成后,再通知displayThread可以显示了,此时displayThread继续执行。
(3) notify()方法:随机通知、唤醒一个在当前对象身上等待的线程。
(4) notifyAll方法:通知、唤醒所有在当前对象身上等待的线程。
6. 守护线程
Daemon后台线程也称为守护线程:当前进程中"所有"、"前台"线程死亡后,后台线程将被强制死亡(非自然死亡),无论是否还在运行。
①守护线程,必须在启动线程前调用。
②main方法也是靠线程运行的,且是一个前台线程。
案例35:
7. 线程的并发安全
多线程在访问同一个数据时(写操作),可能会引发不安全操作。
① 同步异步:
同步:同一时刻只能有一个执行,A和B配合工作,步调一致的处理(B得到A的执行结果才能继续)。如一群人刷卡上公交车。
异步:同一时刻能有多个执行,并发,各自干各自的。如一群人骑自行车。
② synchronized可以修饰方法也可以单独作为语句块存在(同步块)。作用是限制多线程并发时同时访问该作用域。
③ synchronized修饰方法后,会为方法上锁。方法就不是异步的了,而是同步的。锁的是当前对象。
④ synchronized同步块:分析出只有一段代码需要上锁,则使用。效率比直接修饰方法要高。
⑤ 线程安全的效率低,如Vector、Hashtable。线程不安全的效率高,如ArrayList、HashMap 。
案例36: