synchronized关键字的使用
多线程的不安全性,所以有了锁的概念,在ThreadA操作对象时,其他线程都不能操作此对象,待ThreadA释放锁后,其他线程中的某一个线程才被允许操作此对象。
synchronized是JVM层面的锁,因为synchronized是关键字,JVM封装了他所具有的功能。
下面我们就看一看synchronized的使用
1、修饰实例方法
2、修饰静态方法
3、修饰代码块
//静态方法
public static synchronized void incr(){
count ++;
}
//实例方法
public synchronized void incr2(){
count ++;
}
//代码块
public void incr3(){
synchronized (SyncDemo.class){
count ++;
}
}
synchroinzed的使用总结:
- 两种作用范围(类锁 / 对象锁)
- 两种表现形式(方法上 / 代码块)
区别:跨对象跨线程访问
两种特性:共享条件(锁共享) / 互斥条件(代码块执行互斥)
线程给我们带来的好处
多线程可以提高现在多核多线程CPU的利用率。
多线程可以帮助我们更好的优化程序,提高程序的运行效率。
多线程的不安全性
既要保证效率又要保证安全的时代,线程的不安全性问题比较突出。多个线程同时访问一个共享变量时,就可能达不到想要的预期。
public class SyncDemo {
private static int count = 0;
public static void incr(){
count ++;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
new Thread(()->{
SyncDemo.incr();
}).start();
}
TimeUnit.SECONDS.sleep(1);
//不等于1000
System.out.println(count);
}
}
锁的升级
JDK1.6之前synchronized锁属于重量级锁,重量级锁会挂起等待线程,挂起CPU开销比较大,所以在JDK1.6后做了优化,引入了偏向锁、轻量级锁的概念。注意锁智能升级不能降级。
锁的升级顺序如下:
无锁 >> 偏向锁 >> 轻量级锁 >> 重量级锁
假如现在有两个线程ThreadA / ThreadB
-
只用ThreadA线程去访问 – 偏向锁
-
ThreadA和ThreadB两个线程交替访问 – 轻量级锁
-
多个线程同时访问 – 重量级锁
偏向锁和轻量级锁相当无没有锁,为什么锁这两种锁没有锁呢?
偏向锁
偏向锁可以使用JVM参数去关闭,因为在现实场景中很少会出现这种情况。
偏向锁在JVM中的实现:
线程ThreadA首次获得锁对象的时候,会修改锁对象中的对象头,
记录一下:现在锁的状态是偏向锁和自己的线程ID,待ThreadA再次访问时,就不用再获得锁了,可以直接执行被锁的代码块。
轻量级锁
绝大多部分情况在线程获得锁以后,在非常短的时间内会释放锁,线程交替等待时,使用自旋获得锁。
自旋会占用CPU资源,所以在指定的自选次数之后,如果还没有获得轻量级锁,锁会膨胀成重量级锁 (BLOCKED 阻塞状态)
轻量级锁是怎么存储的呢?
ThreadA线程在获得锁后,会创建以个线程栈帧存储锁的对象头,然后修改对象头中存储该线程栈帧的hash值标识自己。如果ThradB获得锁后,也是同样。
重量级锁
没有获得到锁的线程会被阻塞,等待CPU再次调度。
为什么重量级锁会比较消耗CPU?
wait、notify、notifyAll对线程的影响
wait 实现线程的阻塞、会释放当前同步锁
notify/notifyAll会唤醒线程
ThreadA
public class ThreadA extends Thread{
private Object lock = null;
public ThreadA(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
System.out.println("ThreadA start");
try {
//ThreadA.sleep(222222222);
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadA end");
}
}
}
ThreadB
public class ThreadB extends Thread{
private Object lock = null;
public ThreadB( Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
System.out.println("ThreadB start");
lock.notify();
System.out.println("ThreadB end");
}
}
}
共用同一把锁
public class WaitNotifyTest {
private static Object lock = new Object();
public static void main(String[] args) {
ThreadA threadA = new ThreadA(lock);
ThreadB threadB = new ThreadB(lock);
threadA.start();
threadB.start();
}
}
输出结果:
ThreadA start
ThreadB start
ThreadB end
ThreadA end
ThradA获得锁 ——> .wait()后阻塞,释放锁 ——>ThreadB获得锁 ——> 唤醒ThreadA ——> ThreadB释放锁 ——> ThreadA重新获得锁执行代码
wait和sleep
wait会释放锁资源,并且释放CPU
sleep不会释放锁资源,但是会出让CPU