zoukankan      html  css  js  c++  java
  • JAVA多线程之Synchronized关键字--对象锁的特点

    一,介绍

    本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点。

    所谓对象锁,就是就是synchronized 给某个对象 加锁。关于 对象锁 可参考:这篇文章

    二,分析

    synchronized可以修饰实例方法,如下形式:

    1 public class MyObject {
    2 
    3     synchronized public void methodA() {
    4         //do something....
    5     }

    这里,synchronized 关键字锁住的是当前对象。这也是称为对象锁的原因。

    为啥锁住当前对象?因为 methodA()是个实例方法,要想执行methodA(),需要以 对象.方法() 的形式进行调用(obj.methodA(),obj是MyObject类的一个对象,synchronized就是把obj这个对象加锁了)。

    上面代码也可写成这样:

    1 public class MyObject {
    2 
    3     public void methodA() {
    4         synchronized(this){
    5             //do something....
    6         }
    7     }

    三,特点

    使用synchronized关键字同步一个明显的特点是:MyObject类中定义有多个synchronized修饰的实例方法时,若多个线程拥有同一个MyObject类的对象则这些方法只能以同步的方式执行。即,执行完一个synchronized修饰的方法后,才能执行另一个synchronized修饰的方法。

    如下:

     1 public class MyObject {
     2 
     3     synchronized public void methodA() {
     4         //do something....
     5     }
     6 
     7     synchronized public void methodB() {
     8         //do some other thing
     9     }
    10 }

    MyObject类中有两个synchronized修饰的方法。

     1 public class ThreadA extends Thread {
     2 
     3     private MyObject object;
     4 //省略构造方法
     5     @Override
     6     public void run() {
     7         super.run();
     8         object.methodA();
     9     }
    10 }

    线程A执行methodA()

    public class ThreadB extends Thread {
    
        private MyObject object;
    //省略构造方法
        @Override
        public void run() {
            super.run();
            object.methodB();
        }
    }

    线程B执行methodB()

    public class Run {
        public static void main(String[] args) {
            MyObject object = new MyObject();
    
            //线程A与线程B 持有的是同一个对象:object
            ThreadA a = new ThreadA(object);
            ThreadB b = new ThreadB(object);
            a.start();
            b.start();
        }
    }

    由于线程A和线程B持有同一个MyObject类的对象object,尽管这两个线程需要调用不同的方法,但是必须是同步的,比如:线程B需要等待线程A执行完了methodA()方法之后,它才能执行methodB()方法。

    四,结论

    从上可以看出,本文中讲述的 synchronized 锁的范围是整个对象。如果一个类中有多个synchronized修饰的同步方法,且多个线程持有该类的同一个对象(该类的相同的对象),尽管它们调用不同的方法,各个方法的执行也是同步的。

    如果各个同步的方法之间没有共享变量,或者说各个方法之间没有联系,但也只能同步执行,这会影响效率。

    五,应用--使用synchronized避免 因数据不一致性而导致读脏数据的情况

    如下示例:

     1 public class MyObject {
     2 
     3     private String userName = "b";
     4     private String passWord = "bb";
     5     
     6     synchronized public void methodA(String userName, String passWord) {
     7         this.userName = userName;
     8         try{
     9             Thread.sleep(5000);
    10         }catch(InterruptedException e){
    11             
    12         }
    13         this.passWord = passWord;
    14     }
    15 
    16     synchronized public void methodB() {
    17         System.out.println("userName" + userName + ": " + "passWord" + passWord);
    18     }
    19 }

    methodA()负责更改用户名和密码。在现实中,一个用户名对应着一个密码。。。

    methodB()负责读取用户名和密码。

    如果methodB()没有用synchronized 修饰,线程A在调用methodA()执行到第7行,更改了用户名,因某种原因(比如在第9行睡眠了)放弃了CPU。

    此时,如果线程B去执行methodB(),那么读取到的用户名是线程A更改了的用户名("a"),但是密码却是原来的密码("bb")。因为,线程A睡眠了,还没有来得及更改密码。

    但是,如果methodB()用synchronized修饰,那么线程B只能等待线程A执行完毕之后(即改了用户名,也改了密码),才能执行methodB读取用户名和密码。因此,就避免了数据的不一致性而导致的脏读问题。

    其他参考资料:

  • 相关阅读:
    leetcode69 X的平方根的几种解法
    记录一次安装OpenGL的漫长过程
    Java序列化和反序列化
    volatile和synchronized关键字
    netty中的传输
    SpringBoot集成netty实现客户端服务端交互和做一个简单的IM
    netty入坑第一步:了解netty和编写简单的Echo服务器和客户端
    java几种垃圾收集方法和垃圾收集器
    SpringCloud-7-Config
    SpringCloud-6-Zuul
  • 原文地址:https://www.cnblogs.com/hapjin/p/5452663.html
Copyright © 2011-2022 走看看