Synchronized关键字

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

  1. 只用ThreadA线程去访问 – 偏向锁

  2. ThreadA和ThreadB两个线程交替访问 – 轻量级锁

  3. 多个线程同时访问 – 重量级锁

    偏向锁和轻量级锁相当无没有锁,为什么锁这两种锁没有锁呢?

偏向锁

偏向锁可以使用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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章