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

------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()。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值