一定要会的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构建,总是失败,求告诉详细教程。不要发网址,要会的人。不会的