------- android培训、java培训、期待与您交流! ----------
一、创建线程:方式一
1、定义一个类,从Thread继承
2、重写run方法
3、创建线程对象,调用start方法
start方法启动线程,并执行run方法
(一)为什么要重写run方法呢?
Thread类是用来描述线程的,而这个线程要执行的代码就需要用一个方法来存储,就是run方法。(主线程执行的代码存放在main方法)
而Thread类本身的run方法中没有定义任何实现代码,所以只有子类重写run方法,定义自己要实现的内容,线程才有意义。
(二)run方法和start方法的区别
run方法直接被调用的时候,是被主线程执行的,和一般的对象调用方法没什么区别;
而start方法是启动一个线程,然后在新的线程中执行run方法,这时主线程继承执行后面的代码,这就是多线程了。
(三)线程的四种运行状态:
|---sleep(time)------>冻结(时间到了回到运行)
被创建----start()---->运行 |---wait()----------->冻结(notify唤醒了继续运行)
| |
stop()或 stop()
run方法执行完成 |
| |
消亡 <-----------------------
还有一个特殊的状态:临时(堵塞)状态,即具备执行资格,但没有执行权(没有分配到cpu资源)
堵塞和冻结类似,只是堵塞是被动的,而冻结是主动的(放弃执行资格)
二、创建线程:方式二
多次启动一个线程是非法的,特别是当线程已经结束执行后,不能再重新启动。
当我们需要对一个功能多次执行,并操作同一个数据时,这种方式就不能实现了。
(因为必须要创建多个线程才能多次执行这个功能,而没创建一个线程对象,对象中的数据都是自己特有的)
而用创建线程的第二种方式就可以实现:实现Runnable接口。
因为当创建多个线程时,可以把同一对象传递给线程,那么多个线程就可以同时执行对象中的run方法。
(一)创建线程:方式二:
1、定义一个类,实现Runnable接口
2、重写run方法
3、创建这个类的对象。
4、创建一个线程(Thread),并把Runnable接口的实现类的对象作为线程类的构造方法参数传递过去。
5、start启动线程。由于start方法调用的是Runnable接口的实现类的对象中的run方法,所以这个线程可以多次启动
(三) 实现方式和继承方式的区别:
1、实现方式避免了单继承的局限性,他可以在继承其他类的同时使用线程。
2、执行代码存放的位置不同,一个在Runnable接口的实现类的run方法中,一个在Thread类的子类的run方法中。
三、线程安全问题
当多条语句在操作同一线程共享数据时,在一个线程执行这些语句的空档,另一个线程有可能会进来改变共享数据,导致了共享数据的不同步。
(一)解决方案:同步
对操作同一线程共享数据的语句进行加锁,在这些语句执行完之前,不允许其他线程访问锁中的代码。
同步前提:
1、存在多个线程(对多个线程同步)
2、多个线程使用同一把锁
弊端:多个线程需要判断锁,较为消耗资源
(二)在使用多线程时,如何找出线程安全问题?
1、明确哪些代码是多线程运行代码
2、明确共享数据
3、明确多线程运行代码中哪些语句是操作共享数据的
四、同步函数
(一)同步代码块可以找一个共享对象来作为锁,而同步函数用的什么锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this。
而静态同步函数在加载时没有对象,他使用的锁该函数所在类的字节码文件对象(Class对象),类名.class。
(二)静态同步函数在懒汉式单例设计模式中的应用
class Single {
private static Single s = null;
private Single(){}
public static Single getInstance(){
if(s == null){
synchronized (Single.class) {
if(s == null){
s = new Single();
}
}
}
return s;
}
}