多线程:synchronized同步方法

两个线程操作同一个对象里的实例变量,为什么是实例变量?因为局部变量是没有线程安全问题的。

代码如下:

public class HasSelfPrivateNum {
     private  int num = 0;
     public void  addi(String username){    (1)
          try{
               if (username.equals("a")){
                    num = 100;
                    System.out.println("a set over!");
                    Thread.sleep(3000);
               }else {
                    num = 200;
                    System.out.println("b set over!");
               }
               System.out.println( username + " num = " + num);
          }catch (InterruptedException e){
               e.printStackTrace();
          }
     }
}
public class ThreadA extends Thread {
     private HasSelfPrivateNum num;
     public ThreadA(HasSelfPrivateNum num){
          this.num = num;
     }
     @Override
     public void run() {
          super.run();
          num.addi("a");
     }
}
public class ThreadB extends Thread{
     private HasSelfPrivateNum num;
     public ThreadB(HasSelfPrivateNum num){
          this.num = num;
     }
     @Override
     public void run() {
          super.run();
          num.addi("b");
     }
}
public class Run {
     public static void main(String[] args) {
          HasSelfPrivateNum num = new HasSelfPrivateNum();
          // HasSelfPrivateNum num1 = new HasSelfPrivateNum();    (2)
          ThreadA threadA = new ThreadA(num);
          threadA.start();
          ThreadB threadB = new ThreadB(num);    (3)
          threadB.start();
     }
}

执行结果:

a set over!
b set over!
b num = 200
a num = 200

执行结果显然发生了线程安全的问题。

使用synchronized同步方法:

在HasSelfPrivateNum的方法(1)上添加sychronized,即

synchronized public void  addi(String username){...}

此时的执行结果为:

a set over!
a num = 100
b set over!
b num = 200

继续,在之前添加synchronized的基础上,我们将之前两个线程访问同一个对象改为每个线程单独访问一个对象。

将Run类中的(1)的注释打开,将(3)处传入的对象改为num1。

此时的执行结果为:

a set over!
b set over!
b num = 200
a num = 100

可以看到没有线程安全问题,但是执行结果的顺序是交叉的。

    这是因为关键词synchronized取得的都是对象的锁,而不是把一段代码或方法当做锁。所以当两个线程访问同一个对象的时候,这个对象的锁没有释放另一个线程就无法访问,执行结果就会是按照顺序的。

    但是如果两个线程执行的是同一个类的两个对象,那么就会创建两个锁,两个线程分别执行互不影响。所以执行结果就会是交叉的。

脏读

代码如下:

public class PublicVar {

    public String username = "A";
    public String password = "AA";
    synchronized public void setValue(String username,String password){
        try {
            this.username = username;
            Thread.sleep(1000);
            this.password = password;
            System.out.println("current thread = " + Thread.currentThread().getName() + " username = " + username
            + " password = " + password);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
    public void getValue(){
        System.out.println("username = " + username + " password = " + password);
    }

}
public class ThreadA extends Thread {

    private PublicVar publicVar;
    public ThreadA(PublicVar publicVar){
        super();
        this.publicVar = publicVar;
    }

    @Override
    public void run() {
        super.run();
        publicVar.setValue("B","BB");
    }
}
public class Test
{
    public static void main(String[] args) {
        try {
            PublicVar publicVar = new PublicVar();
            ThreadA threadA = new ThreadA(publicVar);
            threadA.start();
            Thread.sleep(500); // 打印结果受此值影响,大于线程threadA(即setValue方法)休眠的时间就不会出现脏读
            publicVar.getValue();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

执行结果:

username = B password = AA
current thread = Thread-0 username = B password = BB

如果也将getValue设为sychronized,那么执行结果:

current thread = Thread-0 username = B password = BB
username = B password = BB

此实验可以得到另个结论:

1,A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object的非sychronized;

2,A线程持持有object对象的Lock锁,B线程如果要调用object的sychronized类型方法则需等待,也就是同步。

第一次执行的时候,线程threadA先获得publicVar对象的锁,但是main线程依然可以调用publicVar对象的非sychronized方法getValue,此时username已被更改,password没被该。

第二次执行的时候,线程threadA先获得publicVar对象的锁,但是main线程在threadA没有执行完成setValue方法之前是不可以调用publicVar对象的sychronized方法getValue的,也就是只有threadA释放了锁,将username和password都赋值了,main线程才可以获取publicVar的锁进而调用getValue方法。

sychronized锁重入

    sychronized关键字拥有锁重入的功能,也就是在一个线程得到一个对象琐时,再次请求此对象锁时是可以得到对象锁的,这也证明了在sychronized方法内部调用本类的其他sychronized方法时,是可以永远得到锁的。

锁异常自动释放

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

猜你喜欢

转载自blog.csdn.net/fanxing1964/article/details/79425318