---------------------- android培训、java培训、期待与您交流! ----------------------
1、多线程概述
进程:一个正在执行中的程序。每一个进程执行都有一个顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:线程就是进程中的一个独立的控制单元。线程控制着进程的执行。一个进程中至少有一个线程。
线程是进程中的内容。执行路径。
JVM启动时会有一个进程java.exe,该进程中至少有一个线程负责程序的执行,而且这个线程运行的代码存在于main方法中,该线程称为主线程。
扩展:其实更细节说明JVM启动时不止一个线程,还有负责垃圾回收机制的线程。
创建线程方式一:继承Thread类
如何在自定义的代码中,自定义一个线程。
通过对API的查找,Java已经提供了对线程这类事物的描述,就thread类。
创建线程的第一种方法,继承Thread类。
步骤:
1、定义类继承Thread。
2、复写Thread类中的run方法。目的:将自定义的代码存储在run方法中,让线程运行。
3、调用线程的start方法,该方法有两个作用:start启动线程,调用run方法。
发现运行结果每一次都不同。
因为多个线程都获取cpu的执行权,cpu执行到谁,谁就运行。
明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)
cpu在做着快速的切换,以达到看上去同时运行的效果。
我们可以形象的把多线程的运行行为是在互相抢夺cpu的执行权,
这就是多线程的一个特性。随机性,谁抢到谁执行,至于执行多长,cpu说了算。
为什么要覆盖run方法呢?[day11-03]
Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法,也就是说,Thread类中的run方法用于存储线程要运行的代码。
start和run的区别:start()开启线程并执行该线程的run方法。run()仅仅是调用方法,而线程创建后并没有运行。
d.start();//开启线程并执行该线程的run方法。start方法调用线程的run方法。 d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。 |
线程的运行状态:[day11-05]
被创建,运行,冻结,消亡。(临时、阻塞状态)
sleep方法需要指定睡眠时间,单位是毫秒。wait(); notify();
一个特殊的状态:就绪(临时状态/阻塞)。具备了执行资格,但是还没有获取资源。
冻结:放弃了执行资格。sleep(); wait(); notify();唤醒后先到临时状态等待执行。
获取线程对象及名称:Thread.currentThread().getName()。
设置线程名称:setName或者构造函数(super)。
线程都有自己的默认名称:Thread-编号(从0开始)。
创建线程的第二种方式:实现Runnable接口。
步骤:
1、定义类,实现Runnable接口。
2、覆盖Runnable接口中的run方法。
将线程要运行的代码存放在该run方法中。
3、通过Thread类建立线程对象。
4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去执行指定对象的run方法。就必须明确该run方法所属的对象。
5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
实现方式和继承方式有什么区别呢?
实现方式的好处:避免单继承的局限性。
在定义线程时,建议使用实现方式。
两种方式的区别:
继承Thread:线程代码存放在Thread子类run方法中。
实现Runnable:线程代码存放在接口的子类的run方法中。
多线程安全问题
导致安全问题的出现的原因:
多个线程访问出现延迟。
线程随机性。
(当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。)
解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
Java对于多线程的安全问题,提供了专业的解决方式。
同步代码块
synchronize(对象)
{
需要被同步的代码
}
对象如同锁,持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
同步的前提:
1、必须要有两个或两个以上的线程。
2、必须是多个线程使用同一个锁。(锁唯一性)
必须保证同步中只能有一个线程在运行。
好处:解决了多线程的安全问题。
弊端:多个线程需要判断,较为消耗资源。当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
如何找到问题?
1、明确哪些代码是多线程运行代码;
2、明确共享数据;
3、明确多线程运行代码中哪些语句是操作共享数据的。
同步函数
格式:
在函数上加上synchronized修饰符即可。
同步函数用的锁是this。
同步函数被静态修饰后,使用的锁是该方法所在类的字节码文件对象。
函数需要被对象调用,那么函数都有一个所属对象引用,就是this。
所有同步函数使用的锁都是this
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证。发现不是this。因为静态方法中不可以定义this。
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
类名.class。 该对象的类型是Class。
静态的同步方法。使用的锁是该方法所在类的字节码文件对象。类名.class
饿汉式更为严谨的写法: class Single { private static final Single s = new single(); private Single(){} public static Single getInstance() { return s; } } |
懒汉式面试考点:在多线程情况下容易发生安全问题。 [day11-14]
懒汉式主要是实例的延迟加载。多线程访问时易出现安全问题,可以使用同步函数或者同步代码块进行解决。但同步函数稍微有些低效。使用同步代码块双重判断可以解决低效,加同步的时候使用的锁是:该类所属字节码文件对象。
class Single { private static Single s = null; private Single(){} public static Single getInstance() { if(s==null) { synchronized(Single.class)//获取锁。 { if (s==null) //---->A; s = new Single(); } } return s; //双重判断提高懒汉式的效率。 } } |
死锁:同步中嵌套同步,而锁却不同。
---------------------- android培训、java培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net/heima