简介
继续上一次的线程01,有问题欢迎指正
1.线程的生命周期
1.新建:创建线程对象,没有调用start
2.准备:线程已经调用start方法,还没有分配到CPU时间片
3.运行:线程分配到了CPU,开始执行run方法
4.阻塞:线程遇到一些情况,暂停执行
5.死亡:run方法执行完
什么情况下会遇到阻塞?
1.调用sleep 2.调用wait 3.调用suspend(废弃) 4.进行IO操作
2.线程的同步问题
出现同步的情况:多个线程同时 操作一个资源(对象、方法等) 上面三个条件很重要 全部都是需要满足的
出现原因:操作系统中,多个线程执行时抢占式的,线程在执行时被其他线程抢占CPU,导致方法没有完全执行完,数据可能被其他线程修改,可能出现数据不一致的情况,这就是线程同步问题。
解决方法:上锁机制:将资源上锁,一个线程执行完,下个线程才能访问。
1.同步方法:一个线程执行方法时,整个方法上锁,其他线程不能调用,线程执行完,其他线程才能进入。在定义方法时,添加关键字。例:
public sychronized 返回类型 方法(...){
}
同步方法的锁对象是this。
2.同步代码块
对一段代码上锁,一个线程进入代码块时,代码块上锁,其他线程不能调用,线程执行完,其他线程才能进入。
sychronized(锁对象){
代码块
}
注意:锁对象可以使任意的成员变量,不能是局部变量,因为局部变量每个线程执行方法时都会创建新的对象,新的对象不能对其他的线程起到作用。
3.同步锁
在java1.5出现,在java.util.concurrent同步包中
Lock接口
实现类:
ReentrantLock -------重入锁
ReentrantReadLock -------读锁
ReentrantWriteLock -------写锁
创建锁:
Lock lock = new ReentrantLock();
注意:该锁对象必须是成员变量
主要方法:
lock() 上锁
unlock() 释放锁(上锁后必须确保能释放锁,否则出现死锁)
代码:
锁.lock();
try{
代码块
}finally{
锁.unlock();
}
性能比较:同步锁 > 同步块 > 同步方法
sychronized机制的原理:
一旦将方法或代码块添加sychronized后,jvm启动monitor(监视器)对进入方法或代码的线程进行监控,当前的线程持有锁,其他线程想进入方法或代码块监视器会进行阻止,只有当前线程执行完,会释放锁,其他线程才能进入。
面试题04:StringBuilder和StringBuffer的区别
两个类用法相同,StringBuffer所有方法上锁,适合多线程,另一个没有上锁,适合单线程。
面试题05:同步方法和同步代码块的区别
1.锁的范围不同(锁粒度不同)
同步方法锁粒度大,锁整个方法,同步块粒度小,锁一段代码
2.性能不同
锁粒度越大,性能越低,同步块性能高于同步方法
3.灵活性不同
同步块可以选择锁某段代码,更灵活
3.单例模式
是什么?是一种经典的设计模式,设计模式是解决某些问题的经验的总结(各个领域)。
单例模式能实现一个类只有一个实例
应用场景:
1.某些特殊业务需求。2.节约系统资源
实现方法:
1.将所有的构造方法定义为私有的private
2.在类中定义一个私有的静态的对象,该对象可以使用new创建。
3.在类中定义一个静态方法,用于返回静态对象
单例模式有两种:
1.饿汉式
一开始就创建对象
问题:如果用户不需要创建对象,对象还是会被创建,消耗了内存
2.懒汉式
一开始不创建对象,调用获得对象的静态方法时,再创建对象,但是懒汉式单例模式存在线程同步问题
public class LazySingleton{
//定义静态对象
private static LazySingleton mySingleton = null;
//将构造方法定义为私有
private LazySingleton(){}
//定义静态方法返回对象
public static LazySingleton getInstance(){
//外层判断,如果对象不为空,就不执行同步块,提高性能。
if(mySingleton == null){
//同步块,保证if判断和创建对象在一个线程里完整执行。
synchronized(LazySingleton.class){
//多个线程可能同时执行这一行,因此需要同步块。
if(mySingleton==null){
mySingleton = new LazySingleton ();
}
}
}
return mySingleton;
}
}
面试题06:需要会手写,以及了解为什么需要两层判断
4.线程死锁
出现情况:两个线程都持有对方的锁,又都需要对方持有的锁,形成循环等待。
避免:编程时不要使用同步块的嵌套
面试题07:利用同步块,写出死锁案例
public class DeadLock{
private Object lock1 = new Object();
private Object lock2 = new Object();
public void func1(){
//当线程运行时,得到lock1这个锁对象
synchronized(lock1){
//该线程需要lock2这个锁对象,但是可能被其他线程拿到了。
synchronized(lock2){}
}
}
public void func2(){
//当线程运行时,得到lock2这个锁对象
synchronized(lock2){
//该线程需要lock1这个锁对象,但是可能被其他线程拿到了。
synchronized(lock1){}
}
}
main{
//这样是概率出现死锁。
DeadLock deadLock = new DeadLock();
new Thread(()->{
deadLock.func1();
}).start();
new Thread(()->{
deadLock.func2();
}).start();
}
}
5.servlet的线程同步问题
Servlet时单实例多线程的
一个Servlet类只有一个实例(节约服务器资源),每个用户访问servlet实例时,服务器会创建新的线程,每个用户的操作相互独立不影响。
多个线程同时访问一个Servlet实例时就可能出现线程同步问题。
解决方法:
1.上锁机制
问题:大量用户都需要排队访问,效率低。
2.使用局部变量
大量用户访问的数据应该使用局部变量,在Servlet中不要使用成员变量。