zoukankan      html  css  js  c++  java
  • 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

    身体是革命的本钱,爱跑步,爱生活!
  • 相关阅读:
    3、取邮件
    9、将标准字符流写到文件中
    8、使用转换流处理标准输入
    6、数据流
    关闭子窗体时刷新父页面
    sql索引规则
    DataTable DataRow[] 排序 多列
    Oracle 自定义函数创建同义词、存储过程或表创建同义词
    C# DataTable 排序
    sql 分组后每组查询10个
  • 原文地址:https://www.cnblogs.com/caozx/p/8874747.html
Copyright © 2011-2022 走看看