• 线程同步 synchronized


    1、了解 synchronized

    在Java中,每一个对象都拥有一个锁标记(monitor),也称为监视器,多线程同时访问某个对象时,线程只有获取了该对象的锁才能访问,这个锁可通过为方法添加关键字 synchronized 来获得,来保证同一对象在同一

    时间只有同一线程在访问,只有等待这个方法执行完毕或者代码块执行完毕,这个线程才会释放该对象的锁,其他线程才能执行这个方法或者代码块。

    2、synchronized的特点

    (1)对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象

    (2)使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

    (3)通过synchronized无法知道有没有成功获取锁。

    3、三种使用方式

    (1)修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁

    public synchronized void increase(){
      i++;
    }

    对于当前类的对象加锁,不同对象有不同的锁,针对同一线程访问的加锁;若是两个对象,则两个对象获取不同的锁,两个线程可以同时进入该方法,以上代码的加锁就会出现线程不安全

    (2)修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁

    public static synchronized void increase(){
       i++;
    }

    作用于静态方法时,其锁就是当前类的class对象锁。由于静态成员不专属于任何一个实例对象,是类成员,即所有该类的对象获取到的都是同一把锁,因此通过class对象锁可以控制静态 成员的并发操作。

    (3)修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

    修饰代码块有以下三种情况:

      1.对给定对象加锁

    public void run() {
            //省略其他耗时操作....
            //使用同步代码块对变量i进行同步操作,锁对象为obj
            synchronized(obj){
                for(int j=0;j<1000000;j++){
                        i++;
                  }
            }
        }

    该类的不同对象获取到obj的锁是同一把锁,两个线程不能进入该方法,保证线程安全

      2.对当前实例对象 this 加锁

    //this,当前实例对象锁
    synchronized(this){
        for(int j=0;j<1000000;j++){
            i++;
        }
    }

    此情况和修饰实例方法的情况相同,有两个线程对象时,会出现线程不安全

      3.对class对象加锁

    //class对象锁
    synchronized(AccountingSync.class){
        for(int j=0;j<1000000;j++){
            i++;
        }
    }

    该类的不同对象获取到的class对象锁是同一把锁,两个线程不能进入该方法,保证线程安全

    4、使用代码

    public class SyncTest extends Thread {
        private int i=0;
        private String str="obj";
    
        public void run(){
            synchronized (str){
                while (i<5){
                    i++;
                    System.out.println(Thread.currentThread().getName()+":"+i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            SyncTest t1=new SyncTest();
            SyncTest t2=new SyncTest();
            t1.start();
            t2.start();
        }
    }

    结果

    Thread-0:1
    Thread-0:2
    Thread-0:3
    Thread-0:4
    Thread-0:5
    Thread-1:1
    Thread-1:2
    Thread-1:3
    Thread-1:4
    Thread-1:5

    线程-1是等线程-0执行完后才执行的

  • 相关阅读:
    Class StatusesTableSeeder does not exist 如何解决
    Heroku 如何上重置 PostgreSQL 数据库
    git强制push
    Heroku登录失败
    将手机替换为*号
    使用TP自带缓存时。出现第一次拿不到数据。
    PHP实现查看邮件是否被阅读
    使用file_get_contents下载图片
    介绍几款开源好用的产品
    查看光纤卡wwn号【转载】
  • 原文地址:https://www.cnblogs.com/yjh1995/p/11714190.html
Copyright © 2020-2023  润新知