一定要会的synchronized关键字的用法

Posted Melody袁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一定要会的synchronized关键字的用法相关的知识,希望对你有一定的参考价值。

synchronized(同步)关键字

修饰方法

  1. 修饰普通方法,锁的是当前实例对象
  2. 修饰静态方法,锁的是当前类的Class对象

修饰代码块,锁的是synchronized括号里的对象


public class SyncDemo1 {
    //修饰普通方法
    public synchronized void method(){

    }
    //修饰静态方法
    public static synchronized void staticMethod(){

    }
    public void someMethod(){
        //同步代码块
        //这里o是局部变量,外部不可能拿到,这种加锁是毫无意义的
        Object o = new Object();
        synchronized (o){
            //o这个引用不能为null
        }
    }

}

synchronized是如何实现加锁的?-----以同步代码块为例
java中的所有对象在jvm实现的时候,内部都包含一把锁,这把锁默认情况下是打开的。
对象可以调用getClass()方法从对象拿到该对象的“类对象”。

同步代码块的理解

synchronized(ref){//加锁
		一些语句;//加锁成功才能执行
}//释放锁

普通同步方法的理解

	 //修饰普通方法
    public synchronized void method(){

    }
    //效果同上
    public void method1(){
        synchronized (this){
            
        }
    }

静态同步方法的理解

	//修饰静态方法
    public static synchronized void staticMethod(){

    }
	//效果同上
    public  void staticMethod1(){
        synchronized (SyncDemo1.class){

        }
    }

获取关于类的对象

  1. 类名.class
  2. 对象的引用.getClass

如果加锁失败,会怎么办?

(会触发线程调度隐含着必须执行操纵系统的代码才能完成这个工作,所有会有切换内核态的操作)

  1. 加锁失败之后没有资格继续执行代码
  2. 占据CPU没有意义
  3. 会触发线程调度,加锁失败的线程,会被调度器从CPU上调度下来
  4. 在锁打开之前,再分配CPU给该线程也没有意义
  5. 线程状态要变化(不是RUNNABLE)
  6. 线程状态编程BLOCKED(这个状态是专为sychronized加锁失败设置的专用状态)
  7. 为了准备好当锁释放时能找到该线程,把它叫回, 把线程加到这把锁的阻塞队列中(blocking queue)

易错点

在这里插入图片描述

哪些情况下会导致两个线程之间产生互斥


public class SyncDemo2 {
	//没加锁
    void method1() {
    }
	//锁的时
    synchronized void method2() {
    }

    static synchronized void method3() {
    }

    void method4() {
        synchronized (this) {
        }
    }

    static void method5() {
        synchronized (SyncDemo2.class) {
        }
    }

    void method6() {
        synchronized (SyncDemo2.class) {
        }
    }

}

在这里插入图片描述

改造n++和n–操作,使得线程安全

只要保证同时加锁,加得是同一把锁,哪个对象都可以
锁得粒度会影响运行效率。



class Adder extends Thread{
    Object o = new Object();
    Adder(Object o){
        this.o = o;
    }


    @Override
    public void run() {
        synchronized (o) {
            for (long i = 0; i < 100_0000_0000L; i++) {

                ThreadDemo.n++;


            }
        }
    }
}

class Suber extends Thread{

    Object o = new Object();
    Suber(Object o){
        this.o = o;
    }
    @Override
    public void run() {
        synchronized (o) {
            for (long i = 0; i < 100_0000_0000L; i++) {

                ThreadDemo.n--;

            }
        }
    }
}
public class ThreadDemo {
    static int n = 0;

    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        Thread a = new Adder(o);
        Thread b = new Suber(o);

        a.start();
        b.start();
        a.join();
        b.join();
        System.out.println(n);
    }

}


当线程A在操作访问锁定的对象时候, 线程B如果要进入synchronized代码块执行的时候是必须等待锁释放的。这样实际上就是将原本并行执行的代码,变为串行执行了。

sychronized能保护什么,起到什么作用

  1. 原子性
  2. 内存可见性(sychronized加锁成功之后,必须做一次从主内存到工作内存得同步操作,保证线程看到的是最新的数据。释放锁之后,必须做一次从工作内存到主内存的刷新操作,保证所有最新的数据写回主内存。用了该关键字也不能保证一定会有同步操作)
  3. 代码有序性
    加锁前的一些语句A
    加锁部分的一些语句B
    加锁后的一些语句C

创建一个线程安全的ArrayList

单线程下不建议使用Vector,因为Vector无脑给所有方法都加锁了,单线程下会有很多加锁和释放锁的操作来耗费时间。


public class ArrayList {
    private int size;
    private final long[]array = new long[100];
    public synchronized void add(long e){
        array[size++] = e;
    }

    public synchronized int size(){
        return size;
    }

    public synchronized long get(int index){
        if (index > 0 || index >= size){
            throw new ArrayIndexOutOfBoundsException();
        }
        return array[index];
    }
}

以上是关于一定要会的synchronized关键字的用法的主要内容,如果未能解决你的问题,请参考以下文章

前端开发注意啦!一定要会的DOM基础操作(上)

测试人一定要会的技能:selenium的三种等待方式解读,清晰明了

C/C++游戏项目:中国程序员一定要会的中国象棋教程

堆和优先级队列3:不泡妹子都要会的LeetCode7道题之一

Maven的这三个用法你一定要会!

minecraft我的世界gradle构建,总是失败,求告诉详细教程。不要发网址,要会的人。不会的