黑马程序员_多线程技术

一些概念:

|--进程:就是一个正在执行中的程序;

|--线程:就是进程中的一个独立的控制单元;线程在控制着进程的执行;

|--一个进程中至少有一个线程;

|--java虚拟机启动的时候会有一个进程java,exe    该进程中至少有一个线程负责java程序的执行,而且这个线程运行的代码存在与main方法中,该线程称为主线程;

|--jvm启动的时候不止一个线程,还有一个垃圾回收的线程,另一个是主线程同时子啊执行;

|--多线程的出现:可以是多部分代码同事进行;-->提高了效率;多条路径同时执行;


创建线程:

|--自己自定义的去创建一些线程,让某些代码可以同时执行

|--如何在自定义一个线程呢?

|--Thread:线程 是程序中的执行线程,Java 虚拟机允许应用程序并发地运行多个执行线程。

|--创建新执行线程有两种方法。一种方法是将类声明为Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。-->第一种方式就是继承Thread类,还要覆盖run方法;

|--步骤:

定义类继承Thread;

复写Thread类中的run方法;目的是就将自定义的代码存储在run方法,让线程运行;

调用线程的start方法;-->该方法有两个作用:启动线程,然后调用run方法;


|--d.run:仅仅是对象调用方法,而线程创建了,但是并没有运行   -->如果是d.run()的话,那么运行的是run里面覆盖的方法,然后在运行主函数里面的方法;

|--d.start:开启线程并执行该线程的run方法;如果是d.start()的话,那么就是多线程的运行状态;

|--另一种方法是声明实现Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。

|--发现每一次运行的结果都是不同的,cpu执行到谁谁就运行,在某一个时刻只能有一个程序运行;cpu做着快速的切换,已达到同时运行的效果;

|--多线程的特性:随机性-->谁抢到谁就执行,执行多长时间,是cpu说了算 


为什么要覆盖run方法呢?

|--Thread类是用来描述线程的;

|--该类就定义了一个功能,用于存储线程要执行的代码,该存储功能就是run方法;也就是说Thread类中的run方法是用来存储线程要运行的代码;


线程的状态

|--被创建也是一种状态

|--运行状态

|--冻结状态

|--消亡状态


获取线程名称和对象

|--线程也有自己的名称:线程都有自己默认的名称:Thread-编号,该编号是从0开始

|--currentThread():获取当前线程对象。该方法是静态的,直接用类名调用;


卖票程序

|--卖票是要多个窗口卖票,要同时出票,多个窗口要同时卖票;

|--


创建线程的第二种方法:

|--接口Runnable:可运行的;里面就只有一个方法;

|--实现Runnable接口;

步骤:

1、定义类实现Runnable接口

2、覆盖Runnable接口中的run方法;-->将线程要运行的代码存放在该run方法中

3、通过Thread类建立线程对象;

4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法;

|--原因:自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去指定指定对象的run方法;

5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法


实现方式和继承方式有什么区别

|--由于java只支持单继承;当你有了自己的父类,但是这时候就不能再继承Thread了,但是你的里面有些代码是多线程锁执行的,那么这个时候,java工程师就对外提供了一个规则,你得符合我的规则我才能帮你办,那么学生就实现了一个接口,

|--通过以上的分析就是学生是人当中的一种,学生像Runnable,学生既继承了人这个类,有实现了Runnable,也就是实现了多线程;对外提供了功能性的扩展

|--实现方式和继承方式有什么区别:实现的方式避免了单继承的局限性,在定义线程时,建议使用实现方式;

|--还有一个就是线程要运行的代码存放的位子是不同的:继承Thread类:线程代码存放代码Thread子类run方法中

实现Runnable接口,线程代码存放在接口子类的run方法中



多线程的安全问题

|--多线程的安全隐患:

|--问题的原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来,导致了共享数据的错误;

|--解决的办法:对多条操作共享数据的语句,只能让一个线程都执行完,再执行过程中其他的线程不可以参与执行;

|--java工程师提供了专业的解决方式:就是同步代码块

|--哪些代码需要同步,就看哪些代码是共享数据;

|--同步的经典例子:火车上的卫生间;

|--对象如同锁,持有锁的线程,可以在同步中执行,没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有锁;

|--同步的前提:必须要有两个或者两个以上的线程,才能要用到同步;

必须是多个线程使用同一个锁;要保证同步中只能有一个线程在运行;要保证你进去了,我就不能进,

|--同步的好处:解决了多线程的安全问题;

|--同步的弊端:多个线程需要判断锁,较为消耗资源;


同步函数用的锁

|--同步函数用的锁是this锁;

静态同步函数的锁

|--同步函数被静态修饰后,使用的锁是哪一个呢?通过验证,发现不是this了;因为静态方法中也不可以是this;

|--静态进内存的时候是没有对象的,是有类的,要封装成class对象,也就是字节码文件对象;

|--静态进内存时,是没有本类对象的,有该类对应的字节码文件对象;类名.class   该对象的类型是Class

|--同步函数被静态修饰后,用的锁是该类所属的字节码文件对象;也就是类名.class;



多线程访问懒汉式

|--懒汉式在进行多线程访问时,会出现安全隐患;

|--如果是同步函数的话,懒汉式如果加了同步会比较低效,每次都要判断锁,

面试懒汉式

问:懒汉式和饿汉式有什么不同?

答:懒汉式的特点在于实例的延迟加载。

问:懒汉式的延迟加载有没有什么问题?

答:懒汉式如果在多线程的访问时会出现安全隐患问题,

问:怎么解决?

答:可以加同步来解决,而加同步的方式,用同步函数和同步代码块都是可以的,但是同步加在同步函数上面会稍微有些低效,这时候用双重判断的形式,可以提高效率;

问:加同步的时候用的锁是哪一个?

答:该类所属的字节码文件对象;


面试的时候要问的;

请写一个延迟加载的单例设计示例;



同步还有一个小小的弊端

同步出现以后会出现的一个现象:

|--死锁现象:就是你持有一个锁,我也持有一个锁,我要到你的你面去运行,就要问你要锁,而你也要我的里面来运行,你也要问我要锁,我不放我的锁要进你的里面去,你不放你的锁要进我的里面来,谁都不放,就出现了死锁的现象,

|--中国人用筷子吃饭的例子:你不放你的筷子,我不放我的筷子,咱们谁也不要吃饭;


|--死锁的出现通常是同步中嵌套同步,而锁却是不同的;-->同步函数里面有同步代码快,同步代码快里面有同步函数;她们用的锁是不相同的;

|--一个线程拿个a锁想要到b里面来,而b锁想要到a里面来;两者都相持不下,就是进不来的; 


面试的时候要求写一个死锁的程序:就写这个locka里面有lockb,lockb里面有locka





多线程间通信

wait()与金额sleep()有什么区别?

wait():释放资源,释放锁

sleep():释放资源,不释放锁;


线程间通讯:其实就是多个线程在操作同一个资源;但是操作的动作不同;因此要放到两个不同的run方法中;



多线程的优先级

|--to String();覆盖了Object里面的toString的方法,建立了自己的方法,

|--返回的是 该线程的字符串的表现形式,包括线程名称优先级线程组;

|--优先级代表着强资源的频率

|--所有的线程,包括主线程的优先级默认都是5;

|--优先级最高是10;

|--最高优先级10,最低优先级1,线程的默认优先级5;


线程的生命周期:

|--有5个状态:被创建,运行,冻结,消亡,阻塞状态或者是临时状态

|--具备执行资格但是没有执行权:临时状态;

|--放弃执行资格:冻结状态

|--既有执行资格,又有执行权的是运行状态











































发布了40 篇原创文章 · 获赞 0 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/java9832/article/details/46523399