synchronize和volatile

https://mp.weixin.qq.com/ssrc=11×tamp=1563424189&ver=1735&signature=vvoQYjKNfiwpMzWjsIiREYSvk2J2KaaFRT7X6RqFAi*NoLzGGElq8Rym8**YYKW6XCKtTWCoHSlM3M0St1LfoUGChTBZx6797bOIKmFpJK6rpvlLWjWGQlwxvJLwUHf1&new=1

(一)synchronize的用法:

   

   可重入原理:加锁计数器:jvm负责跟踪对象被加锁的次数,线程第一个对象加锁时,计数为1,每当这个相同的线程在此对象再次获得锁时,计数会递增,当任务离开时,计数会递减,当计数为0时,锁被完全释放

   保证可见性的原理,内存模型:JSR133定义的其中一条happen-before规则:对一个监视器的解锁操作happens-before于每一个后续对同一个监视器的加锁操作。

即当ThreadA释放锁时,它所写过的变量(比如,x和y,存在它工作内存中的)都会同步到主存中,而当ThreadB在申请同一个锁时,ThreadB的工作内存会被设置为无效,然后ThreadB会重新从主存中加载它要访问的变量到它的工作内存中(这时x=1,y=1,是ThreadA中修改过的最新的值)。通过这样的方式来实现ThreadA到ThreadB的线程间的通信。

Synchronize的缺点:

       1.效率低:锁释放的情况少,试图获取锁时不能设定超时,不能中断一个正在试图获取锁的线程

       2.不够灵活:加锁的时机单一,每个锁仅有单一的条件,可能是不够的

       3.等待的线程不能退出,直至获取到锁

使用时的注意点:

    1.锁对象不能为空:因为锁的信息都在对象头中记录,如果作为锁对象为null,对象头是没有信息的,这样的对象是不能作为锁的

    2.作用域不宜过大:Synchronized的作用域过大,导致大部分代码都串行,效率很低

    3.避免死锁

如何选择Synchronized和Lock?

    能不用就都不用,尽量使用JUC包中提供的工具类,如果Synchronized适用,就用Synchronized,可以减少代码编写,也不会因为粗心而忘记锁释放的问题

多线程访问方法的具体情况:(https://blog.csdn.net/weixin_34247032/article/details/91314307

    1.两个线程访问一个对象的同步方法

package synchronize_volatile;

/*

*  两个线程访问一个对象的同步方法

* */

public class TestSynchronized1 implements  Runnable{

   public synchronized void methord(){

       

        System.out.println("当前线程开始"+Thread.currentThread().getName());

        try {

            Thread.sleep(3000);

        } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

        System.out.println("当前线程结束"+Thread.currentThread().getName());

   }

  @Override

  public void run() {

        methord();

       

   }

   public static void main(String[] args) {

        long startTime =  System.currentTimeMillis();

        TestSynchronized1 t =new  TestSynchronized1();

        Thread thread = new Thread(t);

        Thread thread1 = new Thread(t);

        thread1.start();

        thread.start();

        while(thread.isAlive()||thread1.isAlive()){

           

        }

        long endTime = System.currentTimeMillis();

        System.out.println("总共用时"+(endTime-startTime));

   }

}

----------------------------------------------------------------------------------------------------------分割线,运行结果----------------------------------------------------------------------------------------------

当前线程开始Thread-1

当前线程结束Thread-1

当前线程开始Thread-0

当前线程结束Thread-0

总共用时6000

----------------------------------------------------------------------------------------------------------分割线,结果分析----------------------------------------------------------------------------------------------

分析,总用时6000,也就是说发生了等待,线程2等待线程1运行完才开始运行,所以总共用时6000

2.两个线程访问两个对象的同步方法

package synchronize_volatile;

/*

*  两个线程访问两个对象的同步方法

* */

public class TestSynchronized1 implements  Runnable{

       

   

   @Override

  public void run() {

        synchronized (this) {

            System.out.println("当前线程开始"+Thread.currentThread().getName());

            try {

                Thread.sleep(3000);

            } catch (InterruptedException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

            System.out.println("当前线程结束"+Thread.currentThread().getName());

        }

   }

   public static void main(String[] args) {

        long startTime =  System.currentTimeMillis();

        TestSynchronized1 t =new  TestSynchronized1();

        TestSynchronized1 t1 =new  TestSynchronized1();

        Thread thread = new Thread(t);

        Thread thread1 = new Thread(t1);

        thread1.start();

        thread.start();

        while(thread.isAlive()||thread1.isAlive()){

           

        }

        while(thread.isAlive()||thread1.isAlive()){

           

        }   

        long endTime = System.currentTimeMillis();

        System.out.println("总共用时"+(endTime-startTime));

   }

}

----------------------------------------------------------------------------------------------------------分割线,运行结果----------------------------------------------------------------------------------------------

当前线程开始Thread-1

当前线程开始Thread-0

当前线程结束Thread-1

当前线程结束Thread-0

总共用时3002

----------------------------------------------------------------------------------------------------------分割线,结果分析----------------------------------------------------------------------------------------------

多次运行测试后,发现结果大于等于3000,两个线程之间不存在等待关系,并行处理不受干扰,锁的不是同一个对象

3.两个线程访问两个个对象的静态同步方法

package synchronize_volatile;

/*

*  两个线程访问两个个对象的静态同步方法

* */

public class TestSynchronized3 implements  Runnable{

   public static synchronized void methord(){

       

        System.out.println("当前线程开始"+Thread.currentThread().getName());

        try {

            Thread.sleep(3000);

        } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

        System.out.println("当前线程结束"+Thread.currentThread().getName());

   }

   @Override

   public void run() {

        methord();

   }

   public static void main(String[] args) {

        long startTime =  System.currentTimeMillis();

        TestSynchronized3 t =new  TestSynchronized3();

        Thread thread = new Thread(t);

        Thread thread1 = new Thread(t);

        thread1.start();

        thread.start();

        while(thread.isAlive()||thread1.isAlive()){

           

        }

        long endTime = System.currentTimeMillis();

        System.out.println("总共用时"+(endTime-startTime));

   }

}

----------------------------------------------------------------------------------------------------------分割线,运行结果----------------------------------------------------------------------------------------------

当前线程开始Thread-1

当前线程结束Thread-1

当前线程开始Thread-0

当前线程结束Thread-0

总共用时6003

----------------------------------------------------------------------------------------------------------分割线,结果分析----------------------------------------------------------------------------------------------

多次运行测试后,发现结果大于等于6000线程之间不存在等待关系,对应的锁是同一把

4. 两个线程访问一个对象的同步方法和非同步方法

package synchronize_volatile;

/*

*      两个线程访问一个对象的同步方法和非同步方法

* */

public class TestSynchronized4 implements  Runnable{

   public synchronized void methord(){

       

        System.out.println("当前线程开始"+Thread.currentThread().getName());

        try {

            Thread.sleep(3000);

        } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

        System.out.println("当前线程结束"+Thread.currentThread().getName());

   }

   public  void methord1(){

       

        System.out.println("当前线程开始"+Thread.currentThread().getName());

        try {

            Thread.sleep(3000);

        } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

        System.out.println("当前线程结束"+Thread.currentThread().getName());

   }

   @Override

   public void run() {

        if("Thread-0".equals(Thread.currentThread().getName())){

            methord();

        }else{

            methord1();

        }

       

   }

  public static void main(String[] args) {

        long startTime =  System.currentTimeMillis();

        TestSynchronized4 t =new  TestSynchronized4();

        Thread thread = new Thread(t);

        Thread thread1 = new Thread(t);

        thread1.start();

        thread.start();

        while(thread.isAlive()||thread1.isAlive()){

           

        }

        long endTime = System.currentTimeMillis();

        System.out.println("总共用时"+(endTime-startTime));

   }

}

----------------------------------------------------------------------------------------------------------分割线,运行结果----------------------------------------------------------------------------------------------

当前线程开始Thread-1

当前线程开始Thread-0

当前线程结束Thread-1

当前线程结束Thread-0

总共用时3000

----------------------------------------------------------------------------------------------------------分割线,结果分析----------------------------------------------------------------------------------------------

非同步方法不受影响

5. 两个线程访问一个对象两个不同的同步方法

package synchronize_volatile;

/*

*  两个线程访问一个对象两个不同的同步方法

* */

public class TestSynchronized5 implements  Runnable{

    public synchronized void methord(){

         

         System.out.println("当前线程开始"+Thread.currentThread().getName());

         try {

             Thread.sleep(3000);

         } catch (InterruptedException e) {

             // TODO Auto-generated catch block

             e.printStackTrace();

         }

         System.out.println("当前线程结束"+Thread.currentThread().getName());

    }

    public  synchronized void methord1(){

         

         System.out.println("当前线程开始"+Thread.currentThread().getName());

         try {

             Thread.sleep(3000);

         } catch (InterruptedException e) {

             // TODO Auto-generated catch block

             e.printStackTrace();

         }

         System.out.println("当前线程结束"+Thread.currentThread().getName());

    }

    @Override

    public void run() {

         if("Thread-0".equals(Thread.currentThread().getName())){

             methord();

         }else{

             methord1();

         }

         

    }

    public static void main(String[] args) {

         long startTime =  System.currentTimeMillis();

         TestSynchronized5 t =new  TestSynchronized5();

         Thread thread = new Thread(t);

         Thread thread1 = new Thread(t);

         thread1.start();

         thread.start();

         while(thread.isAlive()||thread1.isAlive()){

             

         }

         long endTime = System.currentTimeMillis();

         System.out.println("总共用时"+(endTime-startTime));

    }

}

6.两个线程访问一个对象的同步方法和静态同步方法

package synchronize_volatile;

/*

*  两个线程访问一个对象的同步方法和静态同步方法

* */

public class TestSynchronized5 implements  Runnable{

    public synchronized void methord(){

         

         System.out.println("当前线程开始"+Thread.currentThread().getName());

         try {

             Thread.sleep(3000);

         } catch (InterruptedException e) {

             // TODO Auto-generated catch block

             e.printStackTrace();

         }

         System.out.println("当前线程结束"+Thread.currentThread().getName());

    }

    public  synchronized static void methord1(){

         

         System.out.println("当前线程开始"+Thread.currentThread().getName());

         try {

             Thread.sleep(3000);

         } catch (InterruptedException e) {

             // TODO Auto-generated catch block

             e.printStackTrace();

         }

         System.out.println("当前线程结束"+Thread.currentThread().getName());

    }

    @Override

    public void run() {

         if("Thread-0".equals(Thread.currentThread().getName())){

             methord();

         }else{

             methord1();

         }

         

    }

    public static void main(String[] args) {

         long startTime =  System.currentTimeMillis();

         TestSynchronized5 t =new  TestSynchronized5();

         Thread thread = new Thread(t);

         Thread thread1 = new Thread(t);

         thread1.start();

         thread.start();

         while(thread.isAlive()||thread1.isAlive()){

             

         }

         long endTime = System.currentTimeMillis();

         System.out.println("总共用时"+(endTime-startTime));

    }

}

7.抛出异常,锁会被释放0

https://blog.csdn.net/u012723673/article/details/80682208

(二)volatile

        volatile特性:保证可见性,不保证原子性,volatile修饰的关键字能避免重排序

 

(三)区别:

        1.volatile本质是下、在高速jvm当前变量咋寄存器当中的值是不确定的,需要从主存中读取,synchronizated则是锁住当前变量,只有当前线程可以访问该变量时,其他线程被阻塞

        2.volatile只能使用在变量级别,而synchronizated可以使用在变量,方法和类级别

        3.volatile可以改变线程的可见性,而Synchronized可以保证线程的可见性和原子性

        4.volatile不会造成线程阻塞,而Synchronized会造成线程阻塞

        5.volatile标记的变量不会被编译器优化,而Synchronized会被编译器优化

        6.当一个域的值依赖他之前的值得时候volatile就无法工作,例如i++

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值