多线程
-------android培训、java培训、期待与您交流!----------
内容: 线程(Thread与Runnable)、同步(synchronized)与锁、
新版锁机制Lock 与 ReentrantLock、守护线程、线程不同状态(运行、睡眠、终止)
1、进程:是一个正在执行中的程序。
每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元;线程在控制着进程的执行。
【比喻:
多线程就是“伪”多个施工队施工公路不同段线路(多核处理才是真有多施工队)】
PS: Java的虚拟机(jvm)执行时除执行main的主线程,还有执行Java垃圾回收机制的线程。
2、线程的创建方式:
第一种:继承Thread类
1、定义类继承Thread,复写Thread类中的run方法;
2、建立子类对象的同时,线程也被创建(但还未启动);
3、调用线程的start方法开启线程
格式:class PrimeThreadextends Thread
public void run( ) {
创建并启动线程:PrimeThread p = new PrimeThread();
- 定义类实现Runnable接口,复写(覆盖)接口中的public void run( )方法;
(同理:将线程要运行的代码存放在该 run方法中。) - 通过Thread类建立线程对象,并将实现Runnable接口的子类对象作为实际参数传递给Thread类的构造函数;
- Thread类对象调用start方法开启线程并调用了Runnable接口子类的run方法。
格式:class PrimeThreadimplements Runnable {
创建并启动线程:
或
思考:为什么要给Thread类的构造函数传递Runnable的子类对象?
答:
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
实现方式和继承方式的区别:
继承Thread:线程代码存放Thread子类run方法中,子类对象调用start( )方法;
实现Runnable:线程代码存在接口的子类的run方法,子类对象作为参数传递给的Thread类对象调用start( )方法。
实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。
3、线程的不同状态:
a、创建
b、运行
c、中断
|——冻结(sleep、wait、notify)——
d、消亡(死亡)—— 程序中run方法结束或通过stop()方法强行关闭,在内存中释放;
每个线程都有自己默认的名称:Thread-编号该编号从0开始。
获取名称:(staticThread)
【标准通用方式;而this虽然也可实现该功能,但仅适用于继承Thread的类。】
(String)
设置线程名称:
Thread.currentThread()和this语句的区别:
Thread.currentThread()
this
(this的三种情况:调用同类的成员(同功能起因)、存在于构造函数第一行、区分同名变量)
结论: 继承Thread类获得当前线程对象可以用Thread.currentThread()和this。
但实现Runnable接口要获取当前线程对象只能使用Thread.currentThread()。
【此为简单说明及结论,详细分析研究可见 CurrentThread.java 中的模拟与分析】
4、线程安全问题:
导致安全问题的出现的原因:
1、多个线程访问出现延迟。
2、线程随机性
注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
5、同步(synchronized)
同步代码块格式:
synchronized(对象){
}
同步可以解决安全问题的根本原因就在那个对象上;该对象如同锁的功能。
如何明确需要同步的代码?
1、明确哪些代码是多线程运行代码;
2、明确共享数据;
3、明确多线程运行代码中哪些语句时操纵共享数据的。(需要同步的代码)
同步的前提:
1、同步需要两个或者两个以上的线程。
2、多个线程使用的是同一个锁。
只有同时满足这两个条件,才能称其为同步,缺一不可。
同步的弊端:
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
同步函数格式:
同步的应用之单例设计模式:懒汉式(双重判断和同步)[延时加载];
注意与之相对的是饿汉式,在实际开发中使用较多,可见之前的“4、面向对象”。】
同步中的锁(对象):
2、this
3、字节码文件对象(类名. class),适用于多数,因为类的字节码文件class是唯一。
同步中的线程等待、唤醒机制:(属于Object类)
(同步对象)
.notify( );
.notifyAll( );
注意:不要同时有两个线程以上在使用同步嵌套同步,因为每个同步对应不同的锁,当持有一个锁而无法获得另一个被其他线程持有锁时,两个线程都处于阻塞状态,从而造成死锁。
思考1:wait( )、notify( )、notifyAll( )用来操作线程为什么定义在了Object类中?
思考2:wait( )、sleep( )有什么区别?
JDK1.5中多线程升级解决方法机制之新版锁机制:
将同步Synchronized替换成现实Lock操作。
将Object中的wait,notify notifyAll,替换了Condition对象。
该对象可以Lock锁进行获取;可实现了本方只唤醒对方操作。
Lock:
创建:
或
(创建互斥锁,还有读写锁ReentrantReadWriteLock)
Condition:替代了Object类中的wait、notify、notifyAll方法;
创建: Condition con = lock . newCondition();
6、停止线程:
- 定义循环结束标记:(避免出现死循环)
- 使用interrupt(中断)方法。
作用:结束线程的冻结状态,使线程回到运行状态中来。
格式:
适用:特殊情况,当线程处于冻结状态时,其读取不到标记,那么线程就无法结束。
注:stop方法已经过时不再使用,因其容易造成bug,存在不安全性。
线程类的其他方法:
1、线程名.setPriority(int num) :
例:
(static int)
2、守护线程:
线程名. setDaemon(boolean b);
(默认为false,即用户线程,主线程main即为用户线程。)
注意:当用户(前台)线程全部结束,剩下正在运行的均为守护线程,则程序终止,JVM结束。
(也可看做后台线程,前台线程完成结束,后台线程也随之结束,程序结束)
3、线程名.join();
应用: 可用来临时加入线程执行。
抛出:throwInterruptedException:
(即当B线程等待状态被中断,try或throws此异常后,B线程将结束中断状态)
4、修改线程状态:
线程类(Thread) . yield();
5、线程对象.toString();
其他提示:
用匿名内部类创建线程的格式:
1、继承Thread类:newThread( ){
2、实现Runnable类:
Runnable r = newRunnable( ) {
newThread(r).start( );
【因为Runnable是接口,且仅有run方法,所以不能直接调用start方法。】