synchronized同步方法

2019-03-24  本文已影响0人  迷糊小生

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

“线程安全”就是已获得的实例变量的值是经过同步处理的,不会出现脏读的现象。

“非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题,所得结果也就是“线程安全”的了,这是方法内部的变量是私有的特性造成的。

package other;

/**
 * 线程不安全
 * @author xml
 *
 */
public class NoThreadSafe {

    private static String userName;
    
    private static String passWard;
    
    /**
     * 模拟Selvet
     */
    public synchronized static void doPost(String userNameRef,String passWardRef) {
        try {
            userName = userNameRef;
            if(userName.equals("aaa")) {
                Thread.sleep(1000);
            }
            passWard = passWardRef;
            System.out.println("userName:" + userName + "----" + "passWard:" + passWard);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
}
public class ThreadTestA extends Thread{

    @Override
    public void run() {
        NoThreadSafe.doPost("aaa", "aaa");
    }
}
public class ThreadTestB extends Thread{

    @Override
    public void run() {
        NoThreadSafe.doPost("bbb", "bbb");
    }
}

public class Test {
    public static void main(String[] args) {
        ThreadTestA aaa = new ThreadTestA();
        aaa.start();
        ThreadTestB bbb = new ThreadTestB();
        bbb.start();
    }
}

结论:在两个线程访问同一个对象中的同步方法时一定是线程安全的。

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

多个线程调用同一个方法时,为了避免数据出现交叉情况,使用synchronized关键字来进行同步。

虽然在赋值时进行了同步,但是在取值的时候有可能会出现脏读的情况,其原因是在读取实例变量时,此值已经被其他线程更改过了。

package other.thread2;

public class PublicBean {

    private String userName = "A";
    
    private String passWord = "AA";
    
    public synchronized void setValue(String userName,String passWord) {
        try {
            this.userName  = userName;
            Thread.sleep(5000);
            this.passWord = passWord;
            System.out.println("setValue method name " + Thread.currentThread().getName()
                    + "---userName:" + userName + "---pwd:" + passWord);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }
    
    public void getValue() {
        System.out.println("getValue method name " + Thread.currentThread().getName()
                + "---userName:" + userName + "---pwd:" + passWord);
    }
}
public class ThreadA extends Thread {
    private PublicBean bean;
    public ThreadA(PublicBean bean) {
        this.bean = bean;
    }
    
    @Override
    public void run() {
        bean.setValue("B", "BB");
    }
}
public class Test {
    public static void main(String[] args) throws InterruptedException {
        PublicBean bean = new PublicBean();
        ThreadA threadA = new ThreadA(bean);
        threadA.start();
        Thread.sleep(200);
        bean.getValue();
    }
}
image.png

出现脏读是因为public void getValue()方法不是同步的,所以可以在任意的时候调用。解决的办法就是加上同步关键字synchronized。

加完后结果如下:


image.png

可见setValue和getValue被依次执行。

总结:当A线程调用anyObj对象加入synchronized关键字的X方法时,A线程就获得了X对象锁,所以其他线程必须等A线程执行完毕后才可以调用X方法,但B线程可以随意调用其他的非synchronized同步方法。

除此之外,关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以在的得到该对象的锁的,这也证明在一个synchronized方法/块的内部调用本类其他synchronized方法/块时,是永远可以得到锁的。

synchronized方法/块内部出现异常锁会自动被释放

上一篇下一篇

猜你喜欢

热点阅读