Java并发系列(九)之互斥同步

19人阅读 评论(0) 收藏 举报
分类:

概要

Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是 JVM 实现的 synchronized,而另一个是 JDK 实现的 ReentrantLock。

synchronized

  • 1.同步一个代码块
public void func () {
    synchronized (this) {
        // ...
    }
}
  • 它只作用于同一个对象,如果调用两个对象上的同步代码块,就不会进行同步。

示例

  • 对于以下代码,使用 ExecutorService 执行了两个线程(这两个线程使用 Lambda创建),由于调用的是同一个对象的同步语句块,因此这两个线程就需要进行同步,当一个线程进入同步语句块时,另一个线程就必须等待。
public class SynchronizedExample {

    public void func1() {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + " ");
            }
        }
    }

    public static void main(String[] args) {
        SynchronizedExample e1 = new SynchronizedExample();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> e1.func1());
        executorService.execute(() -> e1.func1());
    }
}
  • 输出
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
  • 对于以下代码,两个线程调用了不同对象的同步代码块,因此这两个线程就不需要同步。从输出结果可以看出,两个线程交叉执行。
public static void main(String[] args) {
    SynchronizedExample e1 = new SynchronizedExample();
    SynchronizedExample e2 = new SynchronizedExample();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(() -> e1.func1());
    executorService.execute(() -> e2.func1());
}
  • 输出
0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9

2. 同步一个方法

public synchronized void func () {
    // ...
}
  • 它和同步代码块一样,只作用于同一个对象。

3. 同步一个类

public void func() {
    synchronized (SynchronizedExample.class) {
        // ...
    }
}
  • 作用于整个类,也就是说两个线程调用同一个类的不同对象上的这种同步语句,也需要进行同步。
public class SynchronizedExample {

    public void func2() {
        synchronized (SynchronizedExample.class) {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + " ");
            }
        }
    }

    public static void main(String[] args) {
        SynchronizedExample e1 = new SynchronizedExample();
        SynchronizedExample e2 = new SynchronizedExample();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> e1.func2());
        executorService.execute(() -> e2.func2());
    }
}
  • 输出
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

4. 同步一个静态方法

public synchronized static void fun() {
    // ...
}
  • 作用于整个类。

ReentrantLock

public class LockExample {

    private Lock lock = new ReentrantLock();

    public void func() {
        lock.lock();
        try {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + " ");
            }
        } finally {
            lock.unlock(); // 确保释放锁,从而避免发生死锁。
        }
    }
}
public static void main(String[] args) {
    LockExample lockExample = new LockExample();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(() -> lockExample.func());
    executorService.execute(() -> lockExample.func());
}
  • 输出
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
  • ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁,相比于synchronized,它多了一些高级功能:
    1.等待可中断
    当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情,可中断特性对处理执行时间非常长的同步块很有帮助。
    2. 可实现公平锁
    公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁;而非公平锁则不保证这一点,在锁被释放时,任何一个等待锁的线程都有机会获得锁。synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但可以通过带布尔值的构造函数要求使用公平锁。
    3. 锁绑定多个条件
    一个 ReentrantLock 对象可以同时绑定多个 Condition 对象,而在 synchronized 中,锁对象的 wait() 和 notify() 或 notifyAll() 方法可以实现一个隐含的条件,如果要和多于一个的条件关联的时候,就不得不额外地添加一个锁,而 ReentrantLock 则无须这样做,只需要多次调用 newCondition() 方法即可。

synchronized 和 ReentrantLock 比较

1. 锁的实现
synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。
2. 性能
从性能上来看,在新版本的 JDK 中对 synchronized 进行了很多优化,例如自旋锁等。目前来看它和 ReentrantLock 的性能基本持平了,因此性能因素不再是选择 ReentrantLock 的理由,而且 synchronized 有更大的优化空间,因此优先考虑 synchronized。
3. 功能
ReentrantLock 多了一些高级功能。
4. 使用选择
除非需要使用 ReentrantLock 的高级功能,否则优先使用 synchronized。这是因为 synchronized 是 JVM 实现的一种锁机制,JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。并且使用 synchronized 不用担心没有释放锁而导致死锁问题,因为 JVM 会确保锁的释放。

查看评论

并行、并发、同步、互斥

并行 计算机操作系统中的并行,指的是同时存在于内存中的多道作业都处于运行状态。实际上都是宏观上并行,微观上串行,因为这些作业都是开始各自的运行,但都没运行完毕,只是交替地使用cpu。     在操...
  • helianbing
  • helianbing
  • 2016-05-28 11:42:22
  • 2068

【Java】线程并发、互斥与同步

网络上对于线程的解析总是天花龙凤的,给你灌输一大堆概念,考研、本科的操作系统必修课尤甚,让你就算仔细看完一大堆文章都不知道干什么。 下面不取网站复制粘贴,在讲解自己的Java线程并发、互斥与同步之前先...
  • yongh701
  • yongh701
  • 2015-01-17 09:22:15
  • 2690

并发,同步,异步,互斥,阻塞,非阻塞的理解

并发(concurrency):在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。其中两种并发关系分别是同步和互斥。 所谓互斥,是指分布在...
  • IT_LOVER_
  • IT_LOVER_
  • 2016-08-08 18:59:41
  • 1569

Java实现的进程同步与互斥(PV)

  • 2014年05月09日 18:04
  • 10KB
  • 下载

并发执行,进程同步,进程互斥,异步,并行,多线程的区别

1. 几个概念1.1 并发在操作系统的一个时间段中,有几个程序同时处于启动运行到运行完毕之间的状态,且这几个程序都在同一个处理机上运行。并发又有伪并发和真并发:伪并发是指单核处理器的并发,真并发是指多...
  • yjk13703623757
  • yjk13703623757
  • 2017-09-19 13:57:18
  • 675

进程互斥.并发.同步程序

  • 2010年06月03日 13:44
  • 3KB
  • 下载

第五章 并发性:互斥和同步

一、并发1、定义:并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。2、特点操作系统并发程...
  • yangquanhui1991
  • yangquanhui1991
  • 2015-07-30 12:07:30
  • 616

进程的同步与互斥区分

进程的同步与互斥区分
  • xuzhiwangray
  • xuzhiwangray
  • 2016-02-02 16:56:08
  • 1233

JAVA多线程机制之同步与互斥

一个多线程的程序,两个或者多个线程可能需要访问同一个数据资源。这时就必须考虑数据安全的问题,需要线程互斥或者同步。线程的互斥当多个线程需要访问同一资源时,要求在一个时间段内只能允许一个线程来操作共享资...
  • jianggujin
  • jianggujin
  • 2016-01-04 20:12:40
  • 4090

并发性:互斥和同步、死锁和饥饿

一、与并发相关的关键术语: 原子操作:要保证指令的序列作为一个组来操作执行,要么都不执行;要么执行要直接执行到指令完毕,中间不能中断 临界区:是一段代码,在这段代码中进程将访问共享资源,当有一个进程在...
  • u013271921
  • u013271921
  • 2015-05-03 15:55:47
  • 1470
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 1万+
    积分: 1793
    排名: 2万+
    博客专栏
    最新评论