同步

作者: 爱做梦的严重精神病患者 | 来源:发表于2019-03-15 16:54 被阅读0次

  在大多数实际的多线程应用中,两个两个以上线程需要共享同一数据的存取。如果两个线程存取相同的对象,并且每个线程都调用了一个修改该对象状态的方法,可能会产生错误。这样的一个情况通常称为竞争条件

  为了避免多线程引起的对共享数据的讹误,必须学习如何同步存取

  有两种机制防止代码块受到并发访问的干扰。Java语言提供一个synchronized关键字达到这一目的,并且Java SE 5.0引入了ReentrantLock类。synchronized关键字自动提供一个锁以及相关的“条件”,对于大多数需要显示锁的情况,这是很便利的。

1.Lock/Condition对象

锁对象

  使用ReentrantLock保护代码块的基本结构如下,这一结构确保任何时刻只有一个线程进入临界区。一旦一个线程封锁了锁对象,其他任何线程都无法通过lock语句。当其他线程调用lock时,它们被阻塞直到第一个线程释放锁对象

//ReentrantLock对象
myLock.lock();
try{
    ...
}
finally {
     myLock.unLock(); 
}

  锁是可重入的,因为线程可以重复地获得已经持有的锁。锁保持一个持有计数来跟踪对lock方法的嵌套调用。线程在每一次调用lock都要调用unlock来释放锁。由于这一特性,被一个锁保护的代码可以调用另一个使用相同的锁的方法。

条件对象

  通常,线程进入临界区,却发现在某一条件满足之后它才能执行。要使用一个条件对象来管理那些已经获得了一个锁但是却不能做有用工作的线程。
  一个锁对象可以有一个多个相关的条件对象。可以用newCondition方法获得一个条件对象

  以下用银行转账的例子演示条件对象:

private Condition sufficientFunds = bankLock.newCondition();

//转账的方法实现
//from    付款账户
//to      收款账户
//amount  转账金额
public void transfer(int from, int to, int amount) {
        bankLock.lock();//获得锁对象
        try{
              while (accounts[from] < amount)//付款账户余额小于转账金额
                 {
                        //等待,调用await方法
                    sufficientFunds.await();
                  }
              //进行转账操作
              ...
              sufficientFunds.signalAll();
          }
      finally {
          bankLock.unlock();
      }
}

  如果transfer方法发现余额不足,调用sufficientFunds.await()当前线程被阻塞,并放弃锁。此时希望另一个线程可以进行增加账户余额的操作。
  等待获得锁的线程和调用await方法的线程存在本质上的不同。一旦一个线程调用await方法,它进入该条件的等待集。当锁可用时,该线程不能马上解除阻塞。相反,它处于阻塞状态,直到另一个线程调用同一条件上的signalAll方法时为止。

  当另一个线程成功转账时,应该调用sufficientFunds.signalAll()。这一调用重新激活因为这一条件等待的所有线程
  当等待线程从等待集中移出,调度器将再次激活它们,同时它们将试图重新进入该对象。一旦锁成为可用的,它们中的某个将从await调用返回,获得该锁并从被阻塞的地方继续执行
  此时,线程应该再次测试该条件。由于无法确保该条件被满足----signalAll方法仅仅时通知正在等待的线程:此时有可能已经满足条件,值得再次去检测该条件。

  当一个线程调用await时,它没有办法重新激活自身。它寄希望于其他线程,如果没有其他线程调用signalAll来重新激活等待的线程,它就永远不再运行了。
  注意调用signalAll不会立即激活一个等待线程。它仅仅解除等待线程的阻塞,以便这些线程可以再当前线程退出同步方法之后,通过竞争实现对对象的访问。

  另一个方法signal,则是随机解除等待集中某个线程的阻塞状态。

2.synchronized关键字

synchronized方法

  Lock和Condition接口为程序设计人员提供了高度的锁定控制。然而,大多数情况下,不需要那样的控制,并且可以使用一种嵌入到Java语言内部的机制。从1.0版开始,Java中的每一个对象都有一个内部锁。如果一个方法用synchronized关键字声明,那么对象的锁将保护整个方法。

  也就是说:

public synchronized void method() {
      method body
}

等价于

public void method() {
    this.intrinsicLock.lock();
    try {
        method body
        }
    finally{ this.intrinsicLock.unlock();}
}

  内部对象锁只有一个相关条件。wait方法添加一个线程到等待集中,notifyAll/notify方法解除等待线程的阻塞状态。也就是说,调用wait或notifyAll等价于await和signallAll。

  将静态方法声明为synchronized也是合法的。如果调用这种方法,该方法获得相关的类对象的内部锁。例如,如果Bank类有一个静态同步方法,那么当该方法被调用时,Bank.class对象的锁被锁住。因此没有其他线程可以调用同一个类的这个或任何其他的静态同步方法。

synchronized块

  也存在另一种使用synchronized关键字的方式,即synchronized()-----同步块。

 public synchronized void methodA() {
        System.out.println("methodA.....");
    }
 
    public  void methodB() {
        synchronized(this) {
            System.out.pritntln("methodB.....");
        }
    }
 
    public void methodC() {
        String str = "sss";
        synchronized (str) {
            System.out.println("methodC.....");
        }

  methodA()和methodB()用的是同一个锁,因为methodB()的this对象,代表的就是方法调用的对象,所以A和B是同一把锁。而C的锁则是str对象的内部锁。

3.总结

  应该使用哪种?是Lock/Condition对象还是同步方法?

  • 最好既不使用Lock/Condition也不使用synchronized关键字。在许多情况下可以使用java.util.concurrent包中的一种机制,它会为你处理所有的加锁。
  • 如果synchronized关键字适合自己的程序,请尽量使用,这样可以减少编写的代码数量,减少出错的机率。
  • 如果特别需要Lock/Condition结构提供的独有特性是,才使用Lock/Condition。

相关文章

  • Linux sersync day35

    什么是实时同步为什么要实时同步实时同步的原理实时同步的场景实时同步工具选择实时同步案例演示 一、什么是实时同步 实...

  • Spring Cloud——Eureka Server服务同步和

    服务同步 服务同步是Server节点之间的数据同步。分为启动时同步,运行时同步。 启动同步 在EurekaServ...

  • Mac版本有道云笔记 同步失败问题

    Mac 版本 有道云笔记同步异常问题,同步失败,无法同步... 最近使用Mac版本有道云笔记,发现同步文件同步失败...

  • 死磕以太坊源码分析之Fetcher同步

    死磕以太坊源码分析之Fetcher同步 Fetcher 功能概述 区块数据同步分为被动同步和主动同步: 被动同步是...

  • ffmpeg 同步

    音视频同步有三种方式 同步音频到视频 同步到外部时钟 同步视频到音频 同步视频到音频 以audio为基准同步vid...

  • OpenMP多线程——Parallel for

    多线程——线程同步 数据竞争问题 线程互斥同步——critical 线程互斥同步——atmoic 线程互斥同步——...

  • UNIX 的5种IO模型介绍

    IO模型同步、异步、阻塞、非阻塞socket阻塞与非阻塞,同步与异步 同步和异步 同步/异步主要针对C端-同步就像...

  • Redis 主从同步

    同步过程 Redis 支持主从复制分为全量同步和增量同步,首次同步是全量同步,主从同步可以让从服务器从 主服务器备...

  • rsync通过服务同步、Linux系统日志、screen

    rsync通过服务同步 Linux文件同步工具-rsync rsync通过服务同步 Linux文件同步工具-rsy...

  • Java16-4 死锁

    死锁有两种情况1、同步嵌套同步方法中嵌套同步代码块或同步代码块嵌套同步代码块出现的错误

网友评论

      本文标题:同步

      本文链接:https://www.haomeiwen.com/subject/fvqlmqtx.html