zoukankan      html  css  js  c++  java
  • [03] 线程同步 synchronized


    1、线程同步概述

    线程之间有可能共享一些资源,比如内存、文件、数据库等。多个线程同时读写同一份共享资源时,就可能引起冲突,所以引入了线程的“同步”机制。

    所谓同步,就是说线程要有先来后到,排队执行操作,而不是同时进行操作。目的就是为了防止多个线程在访问相同数据对象时,对数据造成污染和破坏。

    为了实现同步,Java中提供了“锁”的机制,可以给共享资源加上一把锁,这把锁只有一把钥匙,哪个线程获取了这把钥匙,才有权利去访问该共享资源。而实现“锁”机制的关键字,就是 synchronized

    2、synchronized

    synchronized 的使用很简单
    • 同步方法:   访问权限修饰符  synchronized 数据返回类型 方法名() { ... }
    • 同步语句块synchronized (共享对象名) { ... }
    • 不能修饰构造函数、抽象方法、成员变量

    下面我们来看个简单的demo:
    //Data 共享数据
    public class Data {
    
        private static int count = 0;
    
        public static int getCount() {
            return count;
        }
    
        public static void add() {
            count++;
        }
    
    }
    
    //Counter 操作共享数据
    public class Counter {
    
        public synchronized void count() {
            Data.add();
            System.out.println("current count = " + Data.getCount());
        }
    
    }
    
    //MyThread
    public class MyThread extends Thread {
    
        private Counter counter;
    
        public MyThread(Counter counter) {
            this.counter = counter;
        }
    
        @Override
        public void run() {
            for(int i = 0; i < 50; i++) {
                counter.count();
            }
        }
    
    }
    
    
    //Test
    public class Test {
        public static void main(String[] args) {
            Counter counter1 = new Counter();
            Counter counter2 = new Counter();
    
            Thread t1 = new MyThread(counter1);
            Thread t2 = new MyThread(counter2);
            t1.start();
            t2.start();
        }
    }
    
    //输出结果示例
    ...
    current count = 2
    current count = 2
    current count = 3
    current count = 4
    current count = 5
    current count = 6
    current count = 7
    ...

    如上例中,可以看到在 Counter 类的 count() 方法我们已经加上了 synchronized 关键字,该方法会将共享数据 Data.count 自增,然后进行打印输出。但是,我们发现输出的结果显示,count 竟然有重复,这意味着数据出现了脏读,我们在打印前,有其他线程对共享数据再次进行了修改!锁机制无效,为什么?

    因为 synchronized 取得的锁是对象锁,而不是把一段代码或方法当成锁。这意味着,要实现同步,即某线程执行,其他线程等待,前提是多个线程访问的是同一个对象。而上例中,显然 counter1 和 counter2 是两个对象,我们的锁也就没有效果。

    我们试着把两个线程的 Counter 类统一下,锁机制就如我们所愿了:
    //Test
    public class Test {
        public static void main(String[] args) {
            Counter counter = new Counter();
            Thread t1 = new MyThread(counter);
            Thread t2 = new MyThread(counter);
            t1.start();
            t2.start();
        }
    }
    
    //输出结果示例
    current count = 1
    current count = 2
    current count = 3
    current count = 4
    current count = 5
    current count = 6
    current count = 7
    current count = 8
    ...

    最后,简单总结:
    • 只对改变共享资源的地方进行同步,而不是所有方法。同步块越大,多线程的效率也越低
    • synchronized 关键字可以修饰方法和代码块,但是不能修饰构造函数、抽象方法、成员变量
    • synchronized 关键字无法继承
    • synchronized 不论在方法还是对象上,取得的锁都是对象,注意搞清楚锁定的是哪个对象


  • 相关阅读:
    Gson中@SerializedName 注解使用
    centos8 安装mongodb4.4
    ssh 连接manjaro失败
    git保存仓库的账号密码
    centos 安装etcd
    kubeadm部署k8s 拉取基础镜像
    centos 安装cloc 代码统计工具
    centos7 安装mongodb
    shell获取时间戳
    最详细的阿里云ECS服务器CentOS7上安装tomcat8服务(三)
  • 原文地址:https://www.cnblogs.com/deng-cc/p/9462739.html
Copyright © 2011-2022 走看看