美文网首页
synchronized同步方法

synchronized同步方法

作者: 迷糊小生 | 来源:发表于2019-03-24 20:15 被阅读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方法/块内部出现异常锁会自动被释放

    相关文章

      网友评论

          本文标题:synchronized同步方法

          本文链接:https://www.haomeiwen.com/subject/xtiyvqtx.html