当前位置:   article > 正文

Synchronized同步方法_同步调用 sy

同步调用 sy

“非线程安全”其实会在多个线程同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”,也就是取到的数据其实是被更改过的。

1**、方法内的变量为线程安全的**
方法内部的私有变量,则不存在“非线程安全”的问题,所得结果也就是“线程安全”的。

2、实例变量非线程安全
如果多个线程共同访问一个对象中的实例变量,则有可能出现“非线程安全”问题。

用线程访问的对象中如果有多个实例变量,则运行的结果有可能出现交叉的情况。
如果对象仅有一个实例变量,则有可能出现覆盖的情况。

1、对象实例

public class HasSelfPrivateNum {
    //实例变量
    private int num=0;

    public void addI(String userName){
        try{
            if(userName.equals("a")){
                num=100;
                System.out.println("a set over");
                Thread.sleep(2000);
            }else{
                num=200;
                System.out.println("b set over");
            }
            System.out.println(userName+"  num="+num);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

为了避免“非线程安全”问题,在方法前加关键字 synchronized.

synchronized public  void addI(String userName)
或者
public synchronized void addI(String userName)
  • 1
  • 2
  • 3

多个线程访问同一个对象中的同步方法时一定是线程安全的。

同步是synchronized, 异步是 asynchronized。
关键字synchronized取得的锁都是对象锁,而不是把一段代码或方法(函数)当做锁。哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁***Lock,那么其他线程只能是等待状态,前提是多个线程访问的是同一个对象*。

但是如果多个线程访问多个对象(不同的对象),则JVM会创建多个锁。

public class Run {
    public static void main(String[] args) {
        //两个不同的对象
        HasSelfPrivateNum has1=new HasSelfPrivateNum();
        HasSelfPrivateNum has2=new HasSelfPrivateNum();
        //两个线程
        CountOperate c1=new CountOperate(has1);
        CountOperate c2=new CountOperate(has2);
        //启动之后,结果是异步执行,与启动顺序无关
        c1.start();
        c2.start();
        try{
             Thread.sleep(5000);
        }catch (InterruptedException e){
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

调用关键字synchronized声明的方法一定是排队运行的(同步的)。只有共享资源的读写访问才需要同步化,如果不是共享资源,根本没有同步的必要。

1、(两个线程分别访问同一个对象的 同步方法和 非同步方法)A线程先持有object对象的Lock锁,B线程可以以异步的方式调用同一个object对象中的 非synchronized类型的方法。(非线程方法的调用不受影响)

2、(两个线程分别访问同一个对象的 两个不同的 同步方法)A线程先持有object对象的Lock锁,B线程如果调用同一个对象的另一个同步方法则需要等待,也就是同步。

三、脏读—一个线程在读取数据,另一个线程在设置数据(同一个对象中,一个是同步方法,一个是非同步)。
发生脏读的情况是在读取实例变量时,此值已经被其他线程更改过了。(对于修改和读取的方法都设置同步synchronized)

四、synchronized锁重入—自己可以再次获得自己的内部锁
在使用synchronized时,当一个线程得到一个对象锁后,(只要该线程还没有释放这个对象锁,)再次请求此对象锁时是可以再次得到该对象的锁的。(也就是同步方法可以调用同一个对象的另一个同步方法)

public class HasSelfPrivateNum {

     public synchronized void addI(String userName){
         //同一个线程中,同一个对象的同步方法
         //可以获得该对象锁Lock
         addA(userName);
    }
     public synchronized void addA(String userName){

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可重入锁也支持在父子类继承的环境中。当存在父子类继承关系时,子类是完全可以通过“可重入锁”调用父类的同步方法。

public class SubHasSelfPrivateNum extends HasSelfPrivateNum {

    public synchronized void addC(String userName){
        //调用父类的同步方法,也可以获得对象锁
        this.addI(userName);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

当一个线程执行的代码出现异常时,其所持有的锁会自动释放。

五、同步不具有继承性 —同步不可以继承

//父类
public class HasSelfPrivateNum {
    //父类的同步方法
     public synchronized void addI(String userName){
    }
}
//子类
public class SubHasSelfPrivateNum extends HasSelfPrivateNum {

    //同名方法,但是不是同步的
    public  void addI(String userName){

        //调用父类的同步方法
        super.addI(userName);
    }

}
多个线程调用子类的方法是异步调用的。但是调用父类的方法,则是同步的
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

思考:如果一开始就调用同步的方法,那么在该同步方法中存在调用其他方法(无论是否同步),都应该是同步的。

//非同步的对象
public class NoSysnTest {
    public  void play(){
         System.out.println("sleep begin threadName="+Thread.currentThread().getName()+" time="+System.currentTimeMillis());
          try{
              Thread.sleep(5000);
              System.out.println("sleep end threadName="+Thread.currentThread().getName()+" time="+System.currentTimeMillis());

          }catch (InterruptedException e){

          }

    }
}

//线程
public class CountOperate extends Thread {
   private HasSelfPrivateNum numRef;
    private NoSysnTest noSysnTest;
    public CountOperate(HasSelfPrivateNum numRef,NoSysnTest noSysnTest){
        this.numRef=numRef;
        this.noSysnTest=noSysnTest;
    }
    public void run(){
    //调用对象的同步方法
       numRef.addI("a",noSysnTest);
    }
}

//同步的对象
public class HasSelfPrivateNum {

     public synchronized void addI(String userName,NoSysnTest noSysnTest){
     //调用非同步的方法
         noSysnTest.play();
    }

}
//执行
public class Run {
    public static void main(String[] args) {
        //两个不同的对象
        HasSelfPrivateNum has1=new HasSelfPrivateNum();
        NoSysnTest noSysnTest=new NoSysnTest();
        //两个线程
        CountOperate c1=new CountOperate(has1,noSysnTest);
        c1.setName("A");
        CountOperate c2=new CountOperate(has1,noSysnTest);
        c2.setName("B");
        //启动之后,结果是异步执行,与启动顺序无关
        c1.start();
        c2.start();
        noSysnTest.play();

    }
}

//结果
//main线程并不是同步的,而是异步的
//A、B线程是同步的,
sleep begin threadName=main time=1520412948959
sleep begin threadName=B time=1520412948969
sleep end threadName=main time=1520412953960
sleep end threadName=B time=1520412953969
sleep begin threadName=A time=1520412953969
sleep end threadName=A time=1520412958969
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/208934
推荐阅读
相关标签
  

闽ICP备14008679号