------------android培训、java培训、java学习型技术博客、期待与您交流!------------
以前没有写笔记的习惯,现在慢慢的发现及时总结是多么的重要了,呵呵。
这一篇文章主要关于java多线程,我这个文章主要关于实际的一些问题。同时也算是我以后复习的资料吧,。呵呵大家多多指教。
同时希望多结交一些技术上的朋友。谢谢。
进程:正在执行的程序。每一个进程执行都有一个执行训醒。该顺序是个执行路径,或控制单元。
线程 :就是进程中的一个独立控制单元,线程控制进程的执行。
一个进程,至少是一个线程。
Java VM启动的时会有一个进程 Java.exe,该进程中至少有一个进程,负责Java程序执行。运行代码存在main()方法中。该线程称之为主线程。
扩展:更细节的说明JVM启动不只一个线程,还有负责垃圾回收机制的线程。
1.如何在自定义代码中建立一个线程呢?
通过API的查找,java已经提供了对线程这类事物的描述 Thread类。
创建线程的第一种方法:继承Thread类
①.定义类,继承Thread
②.复写Thread中的Run方法。目的:将自定义代码存在Run中,让线程运行。
③.调用start方法。有2个作用:1"启动线程 2"调用Run方法。
发现运行结果每一次都不同,
因为多个线程都获取cpu的执行权,cpu执行到谁,谁就执行。
明确一点,在某一时刻,只能有一个程序在运行。(多核除外)
cpu在做快速切换,已达到看上去是同时的效果
我们可以形象的把多线程的运行行为看在互相抢夺cpu的执行权。
这就是多线程的特性:随机性。谁抢到,谁执行。执行多长时间,cpu说的算。
为什么要覆盖Run方法呢?
Thread类用于描述线程,该类就定义了一个功能,用于存储线程要执行的代码。该存储功能就是Run方法。
Thread Run是用于存储线程要运行的代码。
线程都有自己默认的名称。Thread—编号,编号从0开始。用getName();获取
Thread.CurrentThread()==this;
Thread.CurrentThread() 获取当前的进程对象
getName() 获取线程名
setName() 或者 构造函数 设置进程名称
2.创建线程的第二种方法:实现Runnable接口
步骤:
1.定义类实现Runnable接口
2.覆盖Runnable中的Run方法。将线程要运行的代码存放到Run方法中。
3.通过Thread类,创立线程对象
4.将Runnable接口的子类对象作为实际参数传递给Tread类的构造函数。
Why?自定义的Run方法所属的对象是Runnable的子类对象,
所以,要让进程去执行指定的Run方法,就必须明确Run方法的所属对象。
5.调用Thread类的Start()方法开启线程,并调用Runnable中的Run方法。
★★实现方式和继承方式有什么区别?★★
实现方式,避免了单继承的局限性。在建立线程时,建议使用实现方式。资源独立共享。
2种方式的区别:
线程代码存放的位置不同:继承:Thread子类Run方法中。
实现:接口子类Run方法中。
多线程会出现安全问题,
问题的原因:当多余语句操作共享数据时,一个线程对多条语句只执行一部分,另一个线程参与进来执行,导致共享数据错误。
解决办法:对多余操作共享数据的语句,只能让一个进程都执行完,在执行的过程中,其它进程不可以参与执行。
java对于多线程的安全问题,提供了专业的解决方式———同步代码块。
synchronized(对象){ //对象:Object obj=new Object();
需要被同步的代码
}
对象如同锁。持有锁的进程可以在同步中执行。没有锁的线程,即使获取cpu的执行权也进不去,因为没有获取锁。
同步的前提:
1.必须有两个或以上的线程。
2.必须多个线程使用同一个锁。
必须保证同步中只有一个线程,Run方法都放在同步里,相当于单线程。
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源。
如何找到问题:
1.明确哪些代码是多线程运行代码。
2.明确共享数据。✽成员都是共享数据
3.明确多线程运行代码中哪些语句是操作共享数据的。
同步函数用的是哪一个锁呢?
函数需要被对象调用,那么还是都有一个所属对象的引用,就是this。
懒汉式:
如果同步函数被static修饰后,使用的锁是什么?
通过验证,发现不是this,因为static方法中不可以定义this。
静态进内存中,没有本类对象,但一定有该字节码文件对象 类名.class
静态同步方法使用的锁是该方法所在类的字节码文件对象。
死锁:
同步中嵌套同步,而锁不同就会发生死锁。
线程间通信:
其实就是多个线程,操作一个资源;但操作的动作不同。
等待唤醒机制:
wait,notify,notify all。都使用在同步中,需要对持有锁的线程,锁在同步中,所以要使用在同步中,因为只有同步中有锁。
为什么这些操作要定义在Object中呢?
因为这些方法,在操作同步中的线程时,都必须要标识他们所操作线程持有的锁,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒。
也就是说:等待和唤醒必须是同一把锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object中。
生产者和消费者,为什么要用while判断标记?
原因:让被唤醒的线程再一次判断标记。
为什么用notifyall()?
因为只用notify,容易出现只唤醒本方线程的情况,导致程序中的线程都等待。
await() signal() signal all()
JDk1.5中提供了多线程升级解决方案
将同步synchronized替换成实现lock操作
将Object中的wait,notify,notify all替换了condition对象。
该对象可以Lock锁获取。
在该事例中,实现了只唤醒对方的操作
释放锁的动作一定要执行 finally
stop方法已过时
如何停止线程?只有一种,run方法结束
开启多线程运行,运行代码通常都是循环结构
只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:当线程处于冻结状态,就不会读取标记,那么线程就不会结束
interrupt()
当没有指定方式让冻结的线程回复到运行状态时,这时就需要对运行状态进行清除,强制让线程恢复到运行状态中来,这样就是可以操作标记让线程停下来。
setDeamon
join。当A线程执行到b.join()时,那么A线程等待,等B线程执行完,A才会执行,用于临时加入线程。
toString()优先级
yield()暂停