1.基础知识
1.sleep()的作用是在指定的毫秒数内让当前"正在执行的线程"休眠.
2.getId()获取线程的唯一标识
3.停止线程3种方法
- 使用退出标志,正常退出
- 使用stop强行终止线程,已废弃.(他会抛出一个ThreadDeath异常.不需要捕获)
- 使用interrupt中断线程,他不会立即停止线程,而是加一个停止的标记.(在线程中捕获这个标记.进行主动退出)
- 使用抛出异常.try-catch停止线程
4.静态方法 interrupted()返回当前正在运行的线程是否已经中断.,在哪个线程里调就返回哪个线程的中断状态
线程的方法 isInterrupted()返回调用线程是否已经中断,哪个线程对象调用他,他返回哪个线程的中断状态.
5.suspend()方法暂定线程 resume()方法恢复线程,缺点是会占有公共对象的锁和数据不同步
6.yield()使当前线程放弃当前的CPU资源,但放弃时间不确定,有可能刚放弃马上又获得CPU时间片.
7.线程优先级 1-10 , 10最高.优先级高得到的CPU资源较多.但不是绝对的.优先级具有继承性,如A线程启动B线程.则B线程的优先级和A相同.
8.守护线程,是特殊的线程.当进程中不存在非守护线程时,守护线程自动销毁. gc就是守护线程.
9.synchronized 关键字取得的就是对象锁.而一个对象只有一把锁.当一个线程获得一个对象的锁后.可以直接访问他所有的带锁的方法.称为锁的重入机制.可重入锁也支持父子类继承的环境中.既子类拿到对象锁后.可以访问父类的带锁的方法.
10.一个线程执行的代码出现异常时,其所持有的锁会自动释放.
11.锁不能被继承.既父类的同步方法加了锁.在子类重写后却没加锁,那么子类方法不会继承父类的锁.因为锁是针对对象的.创建子类对象时不会创建父类对象.也就不涉及父类对象的锁的方法.
12.同步代码快.可以减少同步等待的时间.不再同步代码块中的代码是异步执行,同步代码块中的代码同步执行.同步代码块也是锁定某个对象的.
13.synchronized关键字加到static方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象加锁.而String具有常量池.不建议对String 加锁.
14.volatile关键字.使变量在多个线程间可见.
- volatile关键字强制从公共内存中读取变量的值.而不使用每个线程的工作内存.
- volatile增加了实例变量在多个线程之间的可见性
- volatile是线程同步的轻量级实现,效率比synchronized高并且volatile只能修饰变量
- 多线程访问volatile不会发送阻塞,而访问synchronized会出现阻塞
- volatile能保证数据的可见性,但不保证原子性(原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行)而synchronized可保证原子性,继而保证可见性
- volatile解决的是变量在多个线程的可见性,synchronized解决的是多个线程访问资源的同步性(个人理解volatile用来读值比较好.写的话会数据错乱,并不能保证同步性.)
15.wait/notify方法在调用前线程必须持有对象锁,这两个方法属于object.wait调用后会释放锁.notify方法执行后,当前线程要把同步方法执行完才会释放锁.之后wait的线程被唤醒,拿到对象锁.如果某线程wait后没有其他线程调用notify.则这个线程会一直等待下去.即时对象锁已经被释放. notifyAll唤醒全部wait的线程.
16 执行完同步代码块会释放锁.执行同步代码块过程中如果遇到异常终止,会释放锁.执行锁对象的wait方法会释放锁.notify方法一次会随机唤醒一个wait方法.
17.通过管道进行线程间通信,可以通过字节流,字符流.
pipeStream (管道流)用于在不同线程间直接发送数据,一个线程发送数据到管道,另一个线程从管道中读取数据.
18.join的作用是等待线程对象销毁.Z线程中调用X.join使所属线程对象X正常执run()方法,而使当前线程Z进行无限期的阻塞,等待线程X销毁后在继续执行Z后面的代码.
join在内部是使用wait方法实现,所以会释放锁.而sleep不会释放锁.
join过程中调用interrupt会抛出异常.
19.threadLocal可以为每个线程单独设置自己的值.inheritableThreadLocal可以让子线程从父线程中取得父线程继承下来的值.
20.ReentrantLock类,用来实现同步功能,支持获得锁,释放锁.
lock.lock() 获得锁 lock.unlock() 释放锁
Condition 是在一个Lock对象里面创建多个Condition(对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知
在调度上更加灵活.既可以唤醒指定线程
Condition的wait,sign方法需要在Lock.lock后调用.
公平锁.线程获取锁的顺序是根据线程枷锁的顺序来分配的,非公平锁,线程获得锁是随机的.ReentrantLock构造函数可以传boolean表示公平锁还是非公平锁
getHoleCount()返回当前线程保持次锁定的个数,也就是调用lock方法的次数.
getQueueLength() 返回等待获取锁的线程的个数.
getWaitQueueLength(Condition condi) 返回等待condition的wait的线程数.
tryLock() 仅在锁未被另一个线程保持时,才获取该锁.
awaitUntil(mills) 在等待mills的到期时间前可以被唤醒.不必非要等待mills长的时间
21.ReenTrantReadWriteLock读写锁
读写锁分为两个锁.读锁,称为共享锁. 写锁,称为排它锁.多个写锁或者多个读锁加写锁互斥.既可以多线程读.只能单线程写.
22.单例模式与多线程结合
1.恶汉模式,静态变量直接创建对象.然后返回
2.懒汉模式.在调用时返回.正规写法是双检查机制
3.私有的静态内部类创建外部类对象.提供方法返回外部类对象.
4.序列化|反序列化单例对象时,需要重写 readResolve方法返回单例对象
5.静态代码块创建单例对象.
6.枚举实现单例模式.(使用枚举类时,构造方法被自动调用,在枚举的构造方法中创建单例类的单例对象)
23.线程的状态
-new 至今尚未启动(创建后还没start)
-runnable 正在java虚拟机中执行(可能获得了CPU时间片,也可能没获得CPU时间片)
-blocked 受阻塞并等待某个锁
-waiting 无限期的等待另一个线程来执行某一特定操作
-timewaitting 在指定时间内等待另一线程执行某一特定操作
-terminated 已退出的线程
1.单例模式的写发
所有的单例模式都是构造函数私有化.成员以private static 修饰,提供公开static 方法访问.不让别人实例化他,
1.恶汉模式.就是加载类时候就加载,缺点是不能有其他实例变量,因为getInstance无法同步.
如果从始至终从未使用过这个实例,则会造成内存的浪费
public class A{
private static A obj =new A(); //直接实例化
也可以使用静态代码块
private static A obj;
static {
obj=new A();
}
private A(){}
public static A getInstance(){
return obj;
}
}
2.懒汉模式.调用时候加载,单线程的时候没事.多线程会产生多个obj对象.
public class A{
private static A obj;
private A(){}
public static A getInstance(){
if(obj ==null){
//初始化东西
obj =new A();
}
return obj;
}
}
3.懒汉模式加 synchronized 关键字. 方法加锁.导致方法需要等待某线程执行完才释放锁.方法执行效率低.
public class A{
private static A obj;
private A(){}
sychnorized public static A getInstance(){
if(obj ==null){
obj =new A();
}
return obj;
}
}
4.另一种懒汉模式加锁.其实和3是一样的
public class A{
private static A obj;
private A(){}
public static A getInstance(){
synchronized(A.class){ //锁住类对象,同时代码块锁要在 obj判断外边.不然没意义.
if(obj ==null){
obj =new A();
}
}
return obj;
}
}
5.懒汉模式加dcl(doublc check lock) 双检查锁机制
public class A {
private volatile static A obj; volatile关键字使所有线程强制去主存储区拿obj变量.
private A () {}
public static A getInstance(){
if(obj ==null){
//这里可以进行一些线程无关的初始化,保证不需要同步的代码异步执行
synchronized(A.class){
if( obj ==null){ //这里判空是为如果有线程阻塞,先前进来的已经创建完毕,就不需要在创建了.
obj=new A();
}
}
}
return obj;
}
}
6.使用静态内部类, 不需要外部类的引用,效果同双检查锁
采用了类装载的机制来保证初始化实例时只有一个线程,
内部类是延时加载的,也就是说只会在第一次调用时加载。不调用就不加载
public class A{
private A(){}
private static class B{
private static A obj =new Object();
}
public static A getInstance{
return B.obj;
}
}
7.枚举方式,在使用枚举时,构造方法被自动调用.不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,
每一个枚举类型和定义的枚举变量在JVM中都是唯一的
public class B{
public enum C{
factory; //枚举类型
private A obj; //需要初始化的参数
private C{ //枚举的构造函数会在调用枚举类型时候执行.
obj =new A();
}
public A getA(){
return obl;
}
}
public static A getInstance(){
return B.factory.getA();
}
}
8.单例模式在序列化时.重新读取会生成新的对象.解决方法如下
public class A {
private staitc A obj =new A();
protected A(){}
/反序列时直接返回当前INSTANCE
private Object readResolve() { //重写这个方法.返回之前创建的
return INSTANCE;
}
}