对象及变量的并发访问
Synchronized
对象锁
在方法(非Static)声明的地方加Synchronized的是当前类的对象
多个对象多个锁
类锁
在静态类上和方法上加锁是在类对应的Class类的单例对象加锁
A线程先持有Object的对象锁,B线程可以异步访问调用Object对象中的非同步方法
出现异常自动释放
重入
一个对象里面两个方法,调用一个带锁的方法时还可以调用第二个带锁的方法,内部互相调用
说明Synchronized使用的“对象监听器”是一个,使用的锁是一个
Synchronized(this)代码块锁的也是当前类对象
suspend和sleep调用完都不释放锁
a = "AA",b = "AA"<br>Synchronized(b)和Synchronized(a)是同一把锁,因为String常量池带来的原因,导致两个变量引用的都是一个,相当于同一个object
会把私有内存中的数据同共有内存中的数据同步,使私有内存的数据和共有内存的数据一致
实例变量是非线程安全的,局部变量是线程安全的
volatile
可见性
使用synchronized就没没必要使用volatile来声明私有变量了
实例化的对象不可以
每次是从共有的内存中读取对象,不是私有内存
原子性
禁止代码重排
volatile int i++的操作是非原子性
从内存中取值i
计算i
将i写入内存
锁的使用
ReentranLock
Lock lock = new ReentrantLock();<br>lock.lock();<br>lock.unlock
具有互斥排他性
await
使用当前线程在接收通知或者别中断之前,一直处于等待状态和wait方法作用是一样的
也会释放锁
signal
公平锁
非公平锁(默认)
trylock
如果在规定时间内持有锁,返回true,拿到的true没拿到的false
嗅探拿锁,如果当前发现锁被其他的线程持有就返回Fals
ReentrantReadWriteLock读写锁
共享锁
lock.readLock().lock();
lock.readLock().unlock();
排他锁
lock.writeLock().lock();
lock.writeLock().unlock();
读写互斥,读读共享,写写互斥
单例与多线程
饿汉
饿汉就是类加载器加载完 我就要吃一口 我就要有这个实例 以后用的都是被咬过的
懒汉
懒汉相当于 别烦我 用的话我就起来一次 给你建好 然后每回用的话 做逻辑判断 是不是空 是空我再起来创建
饿汉模式和静态内部类实现单例模式的优点是写法简单,缺点是不适合复杂对象的创建。对于涉及复杂对象创建的单例模式,比较优雅的实现方式是懒汉模式,但是懒汉模式是非线程安全的,下面就讲一下懒汉模式的升级版——双重构校验锁模式(双重构校验锁是线程安全的)。
为什么是双重校验 ?
第二次校验是为了解决问题①,即避免多个线程重复创建对象。
第一次校验是为了提高效率,避免 INSTANCE 不为null时仍然去竞争锁。
public class Lock2Singleton {
private volatile static Lock2Singleton INSTANCE; // 加 volatile
private Lock2Singleton() {}
public static Lock2Singleton getSingleton() {
if (INSTANCE == null) { // 双重校验:第一次校验
synchronized(Lock2Singleton.class) { // 加 synchronized
if (INSTANCE == null) { // 双重校验:第二次校验
INSTANCE = new Lock2Singleton();
}
}
}
return INSTANCE;
}
为什么加 volatile ?
加 volatile 是为了禁止指令重排序,也就是为了解决问题②,即避免某个线程获取到其他线程没有初始化完全的对象。
static静态代码块
将new对象放在静态代码块中,代码在使用类的时候就已经执行,不会存在重复创建对象
线程池
new TreadPoolExecutor(corePoolSize,MaximumPollSize,KeepAliveTime,TimeUnit.SECONDS,new LinkedLockingQueue)
使用SynchronousQueue
使用LinkedBlockingQueue
KeepAliveTime
当线程数量大于核心线程数,没超过等待时间是不会被从LinkedLockingQueue),如果是超过此时间范围的,删除空闲的线程,<br>范围是max~core 也就是core之外的
Java多线程技能
实现Runnable接口 重写Run方法
继承Tread 类(Runnable实现类) 重写Run方法
线程启动使用 Thread.start() 如果调用的是Tread.run就不是异步执行了
判断线程是不是停止状态
interrupted
currentThread()是否已经中断
判断完之后会把当前状态变为非中断
第一次调用完清除登陆状态
只要interrupt和sleep碰到一块就会出现异常
方法
stop暴力停止
yield()放弃当前Cpu资源
线程优先级有继承性
比如A线程启动B线程AB优先级一样
CPU会优先执行优先级比较高的线程
setPriority
多线程中线程之前切换是随机的
守护线程
随主线程的毁灭而毁灭,相当于JVM中一直有线程做着辅助工作
线程之间的通信
wait/notify机制
wait的作用是使线程暂停运行,不是在notify执行之后立即释放锁,而是等当线程执行完毕后再唤醒<br>必须执行完notify方法所在的同步代码块执行完
notify通知暂停的线程继续运行,仅通知一个,只唤醒等待的同一把锁的wait
notifyAll()会按照wait的倒序以此唤醒全部线程
wait必须要有synchorized,是object的方法
ThreadLocal
线程的Map存放自己的线程,向每个线程存储自己的私有数据,线程安全
定时器
Timer
cancel清除自身外所有任务
多个线程在不同的TimerTask切换的算法的顺序每次将最后一个任务放在队列头,:abc、cab、bca
schedule(TimeTasjk task, Date firstTime, long period):该方法作用是在指定日期后按照指定时间间隔,无限循环执行任务
Long noeTime = System.currentTimeMillis()<br>Long sheduletime = nowTIme+10<br>Mytask extends TimerTask<br>Nytask task = new MyTask()<br>Timer time = New Timer()<br>time.shedule(task,new Date(sheduleTimer))