十 多线程
Posted ltfxy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十 多线程相关的知识,希望对你有一定的参考价值。
一、多线程的引入:
什么是线程?
- 程序执行的一条路径,一个进程中可以包含多个线程
- 多线程并发执行可以提高程序的效率,可以同时完成多项工作
多线程应用场景:
- qq同时和多人聊天
- 服务器同时处理多个客户端请求
二、多线程并行和并发的区别:是否在某一时刻只有1可线程在运行
- 并行:两个任务同时运行,甲任务运行的同时,乙任务也在运行,需要多核CPU
- 并发:两个任务都请求运行,处理器只能处理一个请求,在短时间间隔内轮流进行,使人感觉两个任务同时在运行
三、关于JAVA的多线程
1、Java程序运行原理
Java命令会启动Java虚拟机,启动jvm,等于启动了一个应用程序(进程)。
该进程会启动了一个主线程,然后主线程去调用某个类的main方法
2、jvm的启动是多线程的吗?
JVM开启至少启动了垃圾回收线程和主线程,所以是多线程的
多次启动一个线程是非法的
四、多线程程序的两种实现方式
方式一:继承Thread类
1、继承Thread类,Override:run方法
2、把新线程要做的事情写在run方法中
3、创建线程对象
4、执行start方法,启动新线程
方式二: 实现Runnable接口(建议使用,后期扩展性强)
1、定义一个类实现Runnable接口
2、重写run方法
3、new一个Thread对象,利用Thread类的构造,将Runnable的子类对象当作参数传进去
4、 执行Thread.start(),开启线程
原理(查看源码):
1、Thread实现了Runnable接口,通过Thread的构造函数,传递了Runnable接口的引用target(目标,靶子)
2、通过Thread类的init方法,将传入引用target赋给成员变量target
3、在Thread类的run方法中,如果target不为空,则线程开启时(start())会调用Runnable接口子类对象的run方法
两种方式的区别:
a:继承Thread:由于子类重写了Thread类的run方法,当调用start()时,直接找到子类的run方法
b : 实现Runnable:Thread类的构造函数中传入了Runnable的引用,通过init方法将引用赋给了Runnable类型的成员变量target
在启动线程时,start方法调用run方法,在run方法里,判断target不为空的话,则调用target的run方法, 执行子类重写的run方法
两种方式的好处和弊端:
继承Thread:
好处是:可以直接调用Thread类的方法,代码简单
弊端是:如果已经有了父类,则不能用这种方法,java单继承,多实现
实现Runnable
好处: 由于java单继承多实现,即使线程类有了父类也可以实现接口。
弊端:不能直接使用Thread中的方法,需要先获取线程对象,再通过Thread的构造传入,代码复杂
匿名内部类实现线程的两种的方式:
1、new Thread(){ @override public void run(); }.start();
2、new Thread(new Runnable(){ @override public void run();}){}.start();
五、线程的命名
1、默认: Thread.getName(): 默认Thread-序号(从1开始)
2、构造:Thread(String name):构造命名 name
3、设置:this.setName(String name):
六、获取当前正在执行的线程
Thread.currentThread()
七、线程睡眠
Thread.sleep(毫秒,微妙),控制当前线程休眠若干时间 1秒:1000毫秒:1000微妙:1000纳秒 1秒=10亿纳秒
八、守护线程:
Thread.setDaemon(boolean on)将该线程标记为守护线程或用户线程。
该线程不会单独执行,当其他的非守护线程都执行结束后,自动退出
比如QQ聊天,把QQ退出,聊天界面也会关闭,聊天界面可以理解为守护线程
九、加入线程,插队
join,当前线程暂停,等待指定的线程执行结束以后,当前线程再继续
join(int),插队,等待指定的毫秒之后,两条线程交替执行
十、礼让线程
yield让出cpu
十一、设置线程的优先级
setProperties()设置线程优先级 ,最小是1,最大是10,默认是5
十二、同步代码块
1、什么情况下需要同步
当多线程并发、有多段代码同时执行时,我们希望某一段代码执行的过程中不要切换到其他线程工作。 这时就需要同步。
如果两段代码是同步的,那么同一时间只能执行一段,在一段代码没执行结束之前,不会执行另一段代码。
2、同步代码块
使用synchronized关键字加上一个锁对象来定义一段代码,这就叫同步代码块。
多个同步代码块如果使用相同的锁对象,那么他就是同步的。
锁对象是任意的,但不能是匿名对象,匿名对象不是同一个对象
同步代码块中,用哪个对象锁,就用哪个对象调用wait方法
十三、同步方法及其锁对象
如何保证锁是一致的?
对于继承Thread方式:
在方法上加synchronized关键字
对于静态方法,锁对象是this
对于非静态方法,锁对象是当前类的字节码对象
对于实现Runnable接口方式:
锁对象可以是this,因为只创建了一个Runnable引用对象传进Thread的构造
十四、死锁
多个线程相互等待
避免同步代码块的嵌套
十五、回顾以前的线程安全问题
看源码:Vector、StringBuffer、HashTable、Collection.synchronized(xxx)
Vector是线程安全的,ArrayList是线程不安全的
StringBuffer是线程安全的,StringBuilder是线程不安全的
HashTable是线程安全的,HashMap是线程不安全的
Collection.synchronized(xxx):返回线程安全的集合
十六、多线程之单例设计模式
单例设计模式:保证类在内存中只有一个对象
如何保证类在内存中只有一个对象?
1、控制类的创建,不让其他类类创建本类的对象
2、在本类中定义一个本类的对象
3、提供公共的访问方式
单例设计模式的两种写法:
饿汉式:
private Singleton(){ }
//创建本类对象,私有化,相当于成员变量
private static Singleton s = new Singleton();
//对外提供一个公共的访问方式
public static Singleton getInstance(){
return s;
}
懒汉式:
private Singleton(){ }
private static Singleton s;
public static Singleton getInstance(){
if(s==null){
//线程1等待,线程2等待,线程1执行,线程2执行,有可能造成2次对象的情况
s = new Singleton();
}
return s;
}
其他写法:
private Singleton(){ }
//创建本类对象,私有化,相当于成员变量
private static Singleton s = new Singleton();
//对外提供一个公共的访问方式
public static Singleton getInstance(){
return s;
}
饿汉式和懒汉式的区别:
1 饿汉式是空间换时间(一上来就创建对象),懒汉式是时间换空间(需要判断,浪费时间)
2 在多线程访问时,饿汉式不会创建多个对象,而懒汉式有可能创建多个对象(如果判断的时候产生线程等待)
十七、单例设计模式的应用场景:RunTime类
Runtime类是一个单例类,查看源码发现构造方法私有,并且是饿汉式的单例形式
r.exec("shutdown -s -t 300")://5分钟之后关机
十八、Timer类:计时器,实现了Runnable接口
Timer.schedule(task, delay):在指定的时间做指定的事情
Timer.schedule(task, delay, period);period之后再做同样的事情,比如闹钟
十九、两个线程间的通信
1、什么时候需要通信
多个线程并发执行时,在默认的情况下CPU是随机切换线程的
如果我们希望他们有规律的执行,就可以使用通信,例如每个线程执行一次打印
2、怎么通信
线程等待 wait(),同步代码块中,用哪个对象锁,就用哪个对象调用wait方法
线程唤醒,notify(),notifyAll();
这两个方法必须在同步代码块中执行,并且使用同步锁对象来调用
二十、wait()、Sleep()和notify()
为什么wait和notify方法定义在Object类中?
因为锁对象可以使任意对象,Object是所有类的基类,所以需要这么定义
sleep和wait的区别?
(a)sleep在Thread类中,需要传入参数,就是时间,时间到了就醒来
wait在Object类中,方法可以传入参数,也可以不传入参数 ,传入参数则在参数时间后等待,不传则直接等待
传参时。sleep睡一阵子就起来,wait一阵子之后就睡
(b)slepp在同步函数或者同步代码块中不释放锁(睡着了也抱着锁)
wait在同步函数或这同步代码块中释放锁
二十一、 jdk1.5新特性,互斥锁
1.同步,使用ReenTrantLock类的lock()和unlock()方法进行同步
lock()获取锁,unlock()释放锁
2.通信,
使用ReenTrantLock类的newCondition()方法获取Condition对象
需要等待的时候使用Condition的await()方法,唤醒的时候用signal()方法
不同的线程使用不同的condition,这样就能区分唤醒的时候找哪个线程了
3.常用方法:
ReenTrantLock.lock():获得锁
ReenTrantLock.unlock():释放锁
ReenTrantLock.await():等待
ReenTrantLock.signal():唤醒
二十二、线程组
线程组概述:
Java中使用ThreadGroup来表示线程组,对一批线程进行分类管理,java允许程序直接对线程组进行控制
默认情况下,所有现场都属于主线程组
我们也可以给线程设置分组
二十三、线程的生命周期(此处应有图)
新建:创建线程对象[thread、runnable]
就绪:线程对象已经启动,但没有获得CPU执行权[start()]
运行:获取到了CPU执行权[抢到了CPU执行权]
阻塞:没有CPU的执行权,回到就绪[sleep()、wait()]
死亡:代码运行完毕,线程消亡,变成垃圾 [run()结束、stop(方法过时)]
二十四、多线程实现的第三种方式,callable(了解)
二十五、设计模式之简单工厂模式的概述和使用
简单工厂模式:
又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例
优点:
客户端不需要负责对象的创建,从而明确了每个类的职责
缺点:
这个静态工厂类负责所有对象的创建,如果有对象添加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期维护
二十六、工厂方法模式的概述、使用和了解
工厂方法模式概述:
抽象工厂类定义创建对象的接口,具体对象的创建工作由抽象工厂类的各个实现类来完成
优点:
客户端不需要负责对象的创建,明确了个各类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可
不影响已有代码,便于后期维护,增强了系统的扩展性
缺点:
需要额外的编写代码,增加了工作量
以上是关于十 多线程的主要内容,如果未能解决你的问题,请参考以下文章