首页 > 代码库 > 黑马程序员-多线程
黑马程序员-多线程
多线程
进程:是一个正在执行的程序 。
线程:就是进程中的一个独立的控制单元,线程在控制进程的执行,一个进程中至少有 一个线程。
java VM 启动的时候会有一个进程java.exe。该进程中至少有一个负责java程序的执行,而且这个线程运行的代码存在于main方法中,该线程称之位主线程。
多线程的一个特性;随机性,谁抢到谁执行,至于执行对长,cpu说了算
创建新执行线程有两种方法。一种方法是将类声明为Thread的子类。该子类应重写Thread类的run方法。
1.如何在自定义的代码中,自定义一个线程呢?
通过对api的查找,java已经提供了对线程这类事物的描述,就是Thread类。
创建线程的第一种方式:继承Thread类。
步骤:
1.定义类继承Thread
2.复写Thread类中的run方法
目的;将自定义代码存储在run方法中,让线程运行
3.调用线程的start方法
该方法有两个作用:启动线程,调用run方法(存储要执行的代码)
为什么要覆盖run方法呢?
Thread 用于描述线程。
该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。
也就是说Thread类中的run方法,用于存储线程要运行的代码。
线程的五种状态:
被创建:start()
冻结状态(睡眠,等待):放弃了执行资格 运行,阻塞可以到达
注:在同步代码中,wait()方法会释放锁,sleep()不会释放锁。
临时状态阻塞:具备执行资格,但没执行权 运行,冻结可以到达阻塞
运行:具备执行资格和执行权 创建,冻结,阻塞可以到达运行。
消亡:stop run()方法执行结束。运行-->消亡
创建线程的第二种方式(必须掌握);实现Runnable 接口
步骤;
1.定义类实现Runnble接口
2.覆盖Runnable接口中的run方法
3.通过Thread类建立线程对象
4.将Runnble接口的子类对象作为实际参数传递给Thread类的构造函数;
为什么要将Runnable接口的子类对象传递给Thread的构造函数呢?
因为,Thread类要运行自定义run()的方法,而自定义的run方法所属的对象是Runnable接口的子类对象
Thread的构造函数可以接收Runnable的子类。所以要让线程去执行我们自己的run方法,就将run()所属的
对象传入。然后Thread会去调用对象中的run(),当然这就不是我们要管的了。
5.调用Thread类的start方法开启线程
实现方式和继承方式有什么区别?(重要)
实现的好处是:避免了单继承的局限性,如果对象本身已经继承了一个类,则无法继承再Thread
在定义线程时,建议使用是实现的方式式
两种方式的区别;
继承;线程代码存放在Thread子类run方法中
实现;线程代码存放在Runnable接口子类的run方法中
多线程出现安全问题原因;
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。 如:判断-结论。判断后线程交替,等线程在回来,往往出现问题。
解决办法;
对多条操作共享数数据的语句,只能让一个线程都执行完,在执行过程中其他线程不可以参与执行
就是同步代码块。
synchronized(对象)
{
需要被同步的代码
}
对象如同锁,持有锁的线程可以在同步中执行。
没有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
火车上的卫生间--经典。
同步的前提;
1.必须要有两个或两个以上的线程。
2.必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程在运行。
好处;解决多线程的安全问题
弊端;多线程需要判断,较为消耗资源。
如何找多线程问题;
1.明确哪些代码是多线程运行代码。、
2.明确共享数据
3.明确多线程运行代码中哪些语句是操作共享数据的。
同步函数和同步代码块
两者的区别:
同步函数的锁:同步函数所在的对象的this。
同步代码块:可以自己定义
如果同步函数被静态修饰,那么使用的锁是什么呢?本类的字节码文件
静态方法中不可以用this,静态进内存,内存中没有本类对象,
但是一定有本类对应的自节码文件对象。
单例设计模式。
*/
//饿汉式
class Single
{
private static final Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
//懒汉式
class single
{
private static Single s = null;//懒汉式不可以使用final
private Single(){}
public static Single getInstance()//同步函数加了锁会很低效
{//双重判断,提高点效率
if(s==null){
synchrosized(Single.class)
{
if (s==null)//多条语句同时共享s
-->A
-->B,容易创建多个对象
s= new Single();
}
}
return s ;
}
}
懒汉式与饿汉式的区别:
1.懒汉式延时加载实例对象,什么用什么时候创建对象。
2.多线程访问出现易安全问题。什么问题,判断完s==null后,容易创建多个对象。
问题怎么解决?加同步函数和同步代码块,但是很低效,每次都得判断同步,
可以使用双重判断提高效率,在同步代码块外面再次判断。
同步代码块的琐是该类的字节码文件对象,因为getInstance是静态的,没有this.
死锁出现的原因:
同步中嵌套同步,而锁却不同
package cn.itcast;
public class Demo22死锁 {
/** 死锁的原因:同步中嵌套同步,而锁却不同 *
* 线程1拿到锁2,才释放锁1,线程2要拿到锁1,才释放锁2。
*/
public static void main(String[] args) {
new Thread(new DeadLock(false)).start();
new Thread(new DeadLock(true)).start();
}
}
class Lock{
public static final Lock Lock1 = new Lock();
public static final Lock Lock2 = new Lock();
}
class DeadLock implements Runnable{
boolean flag = false;
DeadLock(boolean flag) {
super();
this.flag = flag;
}
public void run() {
if(flag){
while(true){
synchronized (Lock.Lock1) {
System.out.println("if..a");
synchronized (Lock.Lock2) {
System.out.println("if..b");
}
}
}
}
else{
while(true){
synchronized (Lock.Lock2) {
System.out.println("else..a");
synchronized (Lock.Lock1) {
System.out.println("else..b");
}
}
}
}
}
}