• Synchronized关键字


      synchronized,形容词,意思是同步的。在多线程中经常用到,我们经常遇到多个线程访问同一个 共享资源 ,这时候必须考虑如何维护数据一致性,在java中synchronized关键字被常用于维护数据一致性。synchronized机制是给共享资源上锁,只有拿到锁的线程才可以访问共享资源,这样就可以强制使得对共享资源的访问都是顺序的。

    1、锁的概念

      因为synchronized关键字涉及到锁的概念,所以先来了解一些相关的锁知识。每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法,线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁(无论是通过正常语句退出还是执行过程中抛出了异常)。正如前面所说,对共享资源的访问必须是顺序的,也就是说当多个线程对共享资源访问的时候,只能有一个线程可以获得该共享资源的锁。当线程A尝试获取线程B的锁时,线程A必须等待或者阻塞,直到线程B释放该锁为止,否则线程A将一直等待下去,因此java内置锁也称作互斥锁,也就是说锁实际上是一种互斥机制。

      根据使用方式的不同一般我们会将锁分为对象锁和类锁,两个锁是有很大差别的,对象锁是作用在实例方法或者一个对象实例上面的,而类锁是作用在静态方法或者Class对象上面的。一个类可以有多个实例对象,因此一个类的对象锁可能会有多个,但是每个类只有一个Class对象,所以类锁只有一个。 类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定的是实例方法还是静态方法区别的 。

    2、synchronized修饰的对象

    2.1. 修饰代码块

    被修饰的代码块称为同步语句块,作用的对象是调用这个代码块的对象,相当于对象锁。一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。

    ps:请注意标红的字体,正确理解。

    package sync;
    
    public class SyncThread implements Runnable {
       private int count;
       public SyncThread() {
          count = 1;
       }
    
       public  void run() {
          synchronized(this) {
             for (int i = 0; i < 5; i++) {
                try {
                   System.out.println(Thread.currentThread().getName() + ":" + (count++));
                   Thread.sleep(100);
                } catch (InterruptedException e) {
                   e.printStackTrace();
                }
             }
          }
       }
    
       public int getCount() {
          return count;
       }
    }

    测试调用:

    package sync;
    
    public class Test {
    
        public static void main(String[] args) {
            SyncThread th  = new SyncThread();
            
            new Thread(th,"Thread1").start();
            new Thread(th,"Thread2").start();
            
            //new Thread(new SyncThread(),"Thread3").start();
            //new Thread(new SyncThread(),"Thread4").start();
        }
    
    }

    此时线程1、2调用的是同个对象,线程2在执行,线程1一直在等待,直到线程2运行结束,结果:

     

    对于Thread3和Thread4,调用的是两个对象,互不干扰,并行执行,结果:

    2.2 修饰方法

    2.2.1 修饰一个普通方法

    被修饰的方法称为同步方法,作用的对象是调用这个方法的对象。Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,public synchronized void method(){//方法体}; synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。这里不再举例子,只需要将上面的synchronized放到run方法上。

    2.2.2 修饰一个静态的方法

    其作用的范围是整个静态方法,作用的对象是这个类的所有对象,因为静态方法是属于类的而不属于对象的,相当于在类上加锁。

    package sync;
    
    public class SyncThread implements Runnable {
        private static int count;
        public SyncThread() {
            count = 1;
        }
    
        public void run() {
            method();
        }
    
        public synchronized static void method() {
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ":" + (count++));
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    调用测试:

    package sync;
    
    public class Test {
    
        public static void main(String[] args) {
            SyncThread th  = new SyncThread();
            
            //new Thread(th,"Thread1").start();
            //new Thread(th,"Thread2").start();
            
            new Thread(new SyncThread(),"Thread3").start();
            new Thread(new SyncThread(),"Thread4").start();
        }
    }

    结果:

    2.3修饰一个类

    其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象,是类锁,类的所有对象用的是同一把锁。

    package sync;
    
     class SyncThread implements Runnable {
        private int count;
        public SyncThread() {
            count = 1;
        }
    
        public void run() {
            method();
        }
    
        public void method() {
            synchronized(SyncThread.class){
                for (int i = 0; i < 5; i++) {
                    try {
                        System.out.println(Thread.currentThread().getName() + ":" + (count++));
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    测试调用:

    package sync;
    
    public class Test {
    
        public static void main(String[] args) {
            SyncThread th  = new SyncThread();
            
            //new Thread(th,"Thread1").start();
            //new Thread(th,"Thread2").start();
            
            new Thread(new SyncThread(),"Thread3").start();
            new Thread(new SyncThread(),"Thread4").start();
        }
    }

    结果:

    2.4修饰一个对象

    其作用的范围是synchronized后面括号括起来的部分,作用的对象是synchronized加锁的那个对象,相当于对象锁。

    账户类:

    package sync;
    
    public class Account {
       String name;
       float amount;
    
       public Account(String name, float amount) {
          this.name = name;
          this.amount = amount;
       }
       //存钱
       public  void add(float amt) {
          amount += amt;
          try {
             Thread.sleep(100);
          } catch (InterruptedException e) {
             e.printStackTrace();
          }
       }
       //取钱
       public  void minus(float amt) {
          amount -= amt;
          try {
             Thread.sleep(100);
          } catch (InterruptedException e) {
             e.printStackTrace();
          }
       }
    
       public float getBalance() {
          return amount;
       }
    }

    操作类:

    package sync;
    
    public class AccountOperator implements Runnable {
        
        private Account account; 
        
        public AccountOperator(Account account) {
            this.account = account;
        }
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ":start");
            synchronized(account){
                account.add(5000);
                account.minus(5000);
                System.out.println(Thread.currentThread().getName() + ":" + account.getBalance());
            }
        }
    
    }

    测试类:

    package sync;
    
    public class Test {
    
        public static void main(String[] args) {
            Account account = new Account("zhang san", 10000.0f);
            AccountOperator accountOperator = new AccountOperator(account);
    
            int THREAD_NUM = 5;
            Thread threads[] = new Thread[THREAD_NUM];
            for (int i = 0; i < THREAD_NUM; i ++) {
               threads[i] = new Thread(accountOperator, "Thread" + i);
               threads[i].start();
            }
        }
    }

    结果:

    可以看出来线程执行synchronized(account){}代码块时都是互斥的。还可以看出此时用的是栈存储的堵塞进程。

    当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁:

    class Test implements Runnable
    {
       Object object= new Object();  // 特殊的对象
       {
          synchronized(object) {
             // todo 同步代码块
          }
       }
    
       public void run() {
    
       }
    }

    3、总结

    1,synchronized关键字加在方法、对象或者是代码块上,如果它作用的对象是非静态的,则它取得的锁是针对对象。

    2,synchronized作用的对象是一个静态方法或者是类,则它取得的锁是针对类,该类所有的对象同一把锁。

     

    参考资料:https://blog.csdn.net/luoweifu/article/details/46613015

    身体是革命的本钱,爱跑步,爱生活!
  • 相关阅读:
    CS224n, lec 10, NMT & Seq2Seq Attn
    CS231n笔记 Lecture 11, Detection and Segmentation
    CS231n笔记 Lecture 10, Recurrent Neural Networks
    CS231n笔记 Lecture 9, CNN Architectures
    CS231n笔记 Lecture 8, Deep Learning Software
    CS231n笔记 Lecture 7, Training Neural Networks, Part 2
    pytorch坑点排雷
    Sorry, Ubuntu 17.10 has experienced an internal error
    VSCode配置python插件
    tmux配置与使用
  • 原文地址:https://www.cnblogs.com/caozx/p/8874747.html
Copyright © 2020-2023  润新知