您的位置:首页 > 职场人生

黑马程序员————Java基础之多线程

2015-01-06 09:45 281 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、多线程的概述

要理解多线程,就必须理解线程。而要理解线程,就必须知道进程。
1、进程
是一个正在执行的程序。
每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
2、线程
就是进程中的一个独立的控制单元,线程在控制着进程的执行,只要进程中有一个线程在执行,进程就不会结束。
一个进程中至少有一个线程。
3、多线程
在java虚拟机启动时会有一个java.exe的执行程序,也就是一个进程,该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。JVM启动了除了执行一个主线程,还有负责垃圾回收机制的线程。像这种在一个进程中有多个线程执行的方式,我们就叫做多线程。
4、多线程存在的意义
多线程的出现能让程序产生同时运行的效果,可以提高程序执行效率。

二、创建线程的方式

创建线程共有两种方式:继承方式和实现方式。
1、继承方式
通过查找java的帮助文档,我们发现java中已经提供了对线程这类事物的描述的类——Thread类。这一种方式就是通过继承Thread类,然后复写其run方法的方式来创建线程。
创建步骤:
a、定义类继承Thread。
b、复写Thread中的run方法。目的是为了将自定义代码存储在run方法中,让线程运行。
c、创建定义类的实例对象,相当于创建一个线程。
d、用该对象调用线程的star方法。该方法的作用是:启动线程,调用run方法。
如果对象直接调用run方法,等同于只有一个线程在运行,自定义的线程并没有启动。
覆盖run方法的原因:
Thread类用于描述线程。该类就定义了一个功能,用于存储线程要执行的代码。该存储功能就是run方法。
如下程序:





2、实现方式
使用继承方式有一个弊端,那就是如果该类本来就继承了其他父类,那么就无法通过Thread来创建线程了,这样就有了第二种创建线程的方式:实现Runnable接口,并复写其中的run方法的方式。
创建步骤:
a、定义类实现Runnable的接口
b、覆盖Runnable接口中的run方法。目的也是为了将线程要运行的代码存放在该run方法中。
c、通过Thread类创建线程对象。
d、将Runnable接口的子类对象作为实参传递给Thread类的构造方法。
因为,自定义的run方法所属的对象时Runnable接口的子类对象。所以要让线程去指定对象的run方法,就必须明确该run所述的对象。
e、调用Thread类中的start方法启动线程,start方法会自动调用Runnable接口子类的run方法。
实现方法好处:避免了单继承的局限性,在定义线程时,建议使用实现方式。
如下:





三、两种方式的区别

1、两种创建方法的区别:
继承Thread类:线程代码存放在Thread子类run方法中。
实现Runnable:县城代码存放在接口子类run方法中。
2、几种状态
被创建:等待启动,调用start启动。
运行状态:具有执行资格和执行权。
临时状态(阻塞):有执行资格,但是没有执行权。
冻结状态:遇到sleep(time)方法和wait()方法时,失去执行资格和执行权,sleep方法时间到或者调用notify()方法时,获得执行资格,变为临时状态。
消亡状态:stop()方法,或者run方法结束。
当已经从创建状态到了运行状态,再次调用start()方法时,就失去意义了,java运行时会提示线程状态异常。

四、线程安全问题

1、导致安全问题的出现的原因:
当多天语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
简单的说就两点:
a、多个线程访问出现延迟。
b、线程随机性。
线程安全问题在理想状态下,不容易出现,但是一旦出现对软件的影响是非常大的。
2、解决办法——同步
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行,在java中对于多线程的安全问题提供了专业的解决方式——synchronized(同步)
这里也有两种解决方法,一种是同步代码块,还有就是同步函数,都是利用关键字synchronized来实现。
a、同步代码块用法:
synchronized(对象)
{需要被同步的代码}
同步可以解决安全问题的根本原因就在那个对象上。其中对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程即使获取CPU的执行权,也进不去,因为没有获取锁。
如下代码:



b、同步函数
格式:在函数加上synchronized修饰符即可
那么同步函数用的是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。所以同步函数的锁就是this。
3、同步的前提
a、必须要有两个或者两个以上的线程。
b、必须是多个线程使用同一个锁。
4、同步的利弊
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源。
5、如何寻找多线程中的安全问题
a、明确哪些代码是多线程运行代码
b、明确共享数据
c、明确多线程运行代码中哪些语句是操作共享数据的。

五、静态函数的同步方式

如果同步函数被静态修饰后好,使用的锁是什么呢?
通过验证,发现并不是this,因为静态方法中也不可以定义this。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。如:
类名.class该对象的类型是Class
这就是静态函数所使用的锁,而静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class
懒汉式单例模式示例
 


六、停止线程

在JDK1.5版本之前,有stop停止线程的方法,但升级之后,此方法已经过时。那么现在我们该如何停止线程呢?
只有一种方法,那就是让run方法结束。
1、开启多线程运行,运行代码通常是循环结构的,只要控制住循环,就可以让run方法结束,也就是线程结束。
2、当没有指定的方法让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法interrupt()。
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: