[03] 线程同步 synchronized


一、线程同步概述

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

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

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

二、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 不论在方法仍是对象上,取得的锁都是对象,注意搞清楚锁定的是哪一个对象
相关文章
相关标签/搜索