HIT Software Construction Lab6引发出来对锁的问题的探究

前言

做完lab5开始做lab6了鸭,哈工大计算机学院的学生永不停歇。在做lab6的时候,我在想移动猴子是锁一整个ladder(ADT)还是只锁一个ladder的一个域Monkey数组呢?这两个好像差不多,然后就开始思考锁一个对象和锁其中一个域有什么区别?如果锁了对象对于方法调用有什么影响?回想起上课老师讲的一个类的synchronized方法之间是mutual excluded我就不太明白这到底是怎么回事
我问了隔壁大佬如果锁了对象,类似于:

synchronized (object) {
...
}

这样的做法,两个线程能不能同时调用一个对象的方法,大佬给我的答案是不可以。
但是又回想起上课的时候老师给我们做了练习,大致如下:

List<String> list = ArrayList<>();
synchronized(list){
...
}

在这个例子中别的线程是可以同时调用list.get()方法的
然后再问大佬,大佬就跟我说了一堆我听不懂的东西,然后说自己去吃饭去了,让我自己理解一下
凭借我足以过六一儿童节的智商我表示无法理解这件事
然后我就做了些实验。

实验内容

实验一

如果在两个线程中,A线程中用锁锁了一个对象a,B线程没有锁a,然后调用a的方法,会发生什么?我就用刚刚那个list作为实验内容,代码如下:

public class ServiceA extends Thread {
  private List<String> list;
  public ServiceA(List<String> list) {
    super();
    this.list = list;
  }
  public void run() {
    System.out.println("here is thread a runnning");
    synchronized (list) {
      for(int i = 0;i<100;i++) {
        list.add(String.valueOf(i));
        System.out.println("thread a added "+i);
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {

          }
          }
    }
  }
}
public class ServiceB extends Thread{
  private List<String> list;
  public ServiceB(List<String> list) {
    super();
    this.list = list;
  }
  public void run() {
    System.out.println("here is thread b runnning");
    System.out.println(list.get(0));
    }
}

简单解释一下这两个类,可以看出ServiceA的操作是一个mutator,ServiceB的操作是一个observer接下来的main函数中我会把同一个list传入这两个类的实例中,然后让这两个类作为两个线程跑,且A会在B之前跑,我在A中每次插入之后都会让这个线程sleep,强迫系统调度B线程,如果能同时调用这两个方法的话A中的输出会穿插着B中的输出
接下来是main函数:

public class SynchronizedTestMain {

  public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    ServiceA a = new ServiceA(list);
    ServiceB b = new ServiceB(list);
    a.start();
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
    }
    b.start();
  }
}

失误了……应该少设置点i的,但是结果很明显,如果在一个线程中用synchronized锁住了一个对象,另一个线程是可以调用这个对象的方法的
那么大佬的归不太可能出错的咯,大佬什么意思呢?由此我做了实验二

实验二

我把ServiceB中的语句也用synchronized包起来:

public class ServiceB extends Thread{
  private List<String> list;
  public ServiceB(List<String> list) {
    super();
    this.list = list;
  }
  public void run() {
    System.out.println("here is thread b runnning");
    synchronized (list) {
      System.out.println(list.get(0));
    }
  }
}

结果就是顺序执行

这里虽然输出了threadB在运行,但是并没有输出list的第一个元素,说明被block了(才不是我写代码失误然后懒得改了哼!)

那么结论已经出来了,如果两个线程同时锁了一个对象,那么一次只有一个线程能够使用这个对象的方法,如果只有一个线程锁了,另一个线程是能够使用这个对象的方法的。
照这个理论来说,两个线程是不可以分别同时使用一个对象的两个synchronized方法的,因为synchronized方法相当于在函数体最外围包一个synchronized(this){}
那么就来做实验看看吧~
实验三

public class TextExample  {
  String text = "text";
  public synchronized void insert(String ins, int pos) {
    System.out.println("here is "+Thread.currentThread().getName()+" inserting");
    System.out.println("now "+Thread.currentThread().getName()+" starts sleeping");
    try {
      int i = 0;
      while(i<3) {
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName()+" has slept for "+i+" secs");
        i++;
      }
    } catch(InterruptedException e) {
    }
    text = text.substring(0,pos)+ins+text.substring(pos);
    System.out.println("now "+ Thread.currentThread().getName()+" has finished inserting");
  }
  public synchronized void delete(int pos, int len) {
    System.out.println("here is "+Thread.currentThread().getName()+" deleting");
    System.out.println("now "+Thread.currentThread().getName()+" starts sleeping");
    try {
      int i = 0;
      while(i<3) {
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName()+" has slept for "+i+" secs");
        i++;
      }
    } catch(InterruptedException e) {

    }
    text = text.substring(0, pos)+text.substring(pos+len);
    System.out.println("now "+ Thread.currentThread().getName()+" has finished deleting");

  }
}
public class ThreadExample extends Thread {
  private TextExample txteg;
  private int choice;
  public ThreadExample(TextExample txteg, int i) {
    super();
    this.txteg = txteg;
    choice = i;
  }

  @Override
  public void run() {
    if(choice == 1) {
      txteg.insert("====", 2);
    }
    else if(choice == 2) {
      txteg.delete(2, 4);
    }
  }
}
public class SynchronizedExampleTest {

  public static void main(String[] args) {
    TextExample textExample = new TextExample();
    ThreadExample threadExample1 = new ThreadExample(textExample, 1);
    threadExample1.setName("thread1");
    ThreadExample threadExample2 = new ThreadExample(textExample, 2);
    threadExample2.setName("thread2");
    threadExample1.start();
    try {
      Thread.sleep(1000);
    }catch(InterruptedException e) {

    }
    threadExample2.start();
  }
}

这次我用了老师上课给的synchronized方法的例子,具体见MIT 6.031 Reading 21
我在textExample里面让他多睡了一会儿然后输出,方便观察,如果能同时运行的话,输出应该是在1睡的时候2能够对字符串进行修改,否则2只能等1睡完才能对字符串进行修改

顺序执行,说明我之前想的没错,问题完美解决~

结语

这三个实验得出来的结论如下:

  1. 如果两个线程A和B同时锁了一个对象Object,那么一次只有一个线程能够使用Object的方法,如果只有一个线程,比如A锁了Object,另一个线程B没有锁Object,那么B是能够使用Object的方法的。
  2. 两个线程不能同时使用一个对象的两个synchronized方法,因为synchronized方法相当于在函数体外面包一个synchronized(this){}

这次实验做下来感觉很舒服,自己探索问题并且得出结论的感觉真的很棒,虽然这个问题好像很基础的样子,我可真是太菜了……
果然老师说的有什么问题不懂java都能告诉我答案,想不明白就去做实验吧~
========================================手动分割线========================================
经过和FGS同学的探讨之后,他提供给了我一个新的角度去理解这个问题,我觉得很不错:
本质上说synchronized(Object){A}锁的就是代码块A,可以将Object看作是一个钥匙,用来开这个锁,一个钥匙一次只能在一个线程的手上。
如果两个线程,比如A和B都用了同一个锁锁住了各自的代码块,假设A先抢到了钥匙,那么没抢到钥匙的那个线程B就得等抢到钥匙的那个线程A先执行完自己代码块当中的内容后,A将钥匙归还,B才能拿到钥匙,才能执行自己代码块中的内容。
锁本质上锁的是对代码块中的内容的权限,所以就算换一个对象锁也没什么问题。这也就解释了为什么如果线程B中不用锁锁上,是不影响调用Object的方法的。

原文地址:https://www.cnblogs.com/SebastianLiu/p/10957788.html

时间: 2024-10-19 10:32:32

HIT Software Construction Lab6引发出来对锁的问题的探究的相关文章

HIT Software Construction Lab 3

? 2019年春季学期 计算机学院<软件构造>课程 Lab 3实验报告 姓名 刘帅 学号 1170500804 班号 1703008 电子邮件 [email protected] 手机号码 目录 1 实验目标概述··· 1 2 实验环境配置··· 1 3 实验过程··· 1 3.1 待开发的三个应用场景··· 1 3.2 基于语法的图数据输入··· 2 3.3 面向复用的设计:CircularOrbit· 2 3.4 面向复用的设计:Track· 4 3.5 面向复用的设计:L· 4 3.6

software construction in Java Lab6

Lab 6 主要解决的是猴子过河的问题. 本实验的场景是这样的,有一条河,河上有n个梯子,每个梯子的长度为h,现在每隔t秒生成k个猴子(速度,方向均是随机的),猴子的总数一共是N个,猴子一共有三个属性,ID ,方向,还有速度,猴子通过选择不同的梯子,过河,到达河对岸即算成功. 具体的细节解释: 每个猴子过河的速度不同,其速度??定义为每秒钟可爬过的踏板的数量.在 独占一部梯子过河的情况下,一只速度为??的猴子过河所需的时间为? ?? 秒.如果有 另一只猴子在它前方但速度较慢,则它的行进速度不得不

Oracle insert hang引发的行锁等待

问题描述: 客户方前台应用某个操作hang住,无报错也未有操作正常结束提示. 查看锁信息如下: SQL> with lk as (select blocking_instance||'.'||blocking_session blocker, inst_id||'.'||sid waiter 2              from gv$session 3              where blocking_instance is not null 4                and

Software Construction Series(2)

Collections.unmodifiable*()与Rep Exposure JDK在collections接口中提供了一类静态函数:collections.unmodifiable*(),其中*匹配Collections的子类. 如图: 简而言之:这类函数返回一个相应对象的不可更改的Copy. 问题在于不可更改性到那种程度: 1.相当于final?(即reference不可变) 2.相当于imutable?(即集合本身内容不可变,其中元素仍可变) 3.相当于can't do anythin

SQL Server锁分区特性引发死锁解析

原文:SQL Server锁分区特性引发死锁解析 锁分区技术使得SQL Server可以更好地应对并发情形,但也有可能带来负面影响,这里通过实例为大家介绍,分析由于锁分区造成的死锁情形. 前段时间园友@JentleWang在我的博客锁分区提升并发,以及锁等待实例中问及锁分区的一些特性造成死锁的问题,这类死锁并不常见,我们在这里仔细分析下.不了解锁分区技术的朋友请先看下我的锁分区那篇实例. Code(执行测试脚本时请注意执行顺序,说明) 步骤1 创建测试数据 use tempdb go creat

游戏动画中欧拉角与万向锁的理解(转)

http://blog.csdn.net/huazai434/article/details/6458257 我在2009年5月份左右拜读了<3D数学基础:图形与游戏开发>,当时对欧拉角中万向锁的概念一直是百思不得其解,也从未遇到过这种情况.书上有这样一句话:"如果您从来没有遇到过万向锁情况,你可能会对此感到困惑,而且不幸的是,很难在本书中讲清楚这个问题,你需要亲身经历才能明白."今天我用3个多小时的时间再次回想了一下这个问题,总算想明白了,现在把思考的结果分享给大家. 下

游戏动画中欧拉角与万向锁的理解

总结:如果动画师在某个关键帧处指定了会引发万向锁的方位,则下一个关键帧的方位一旦超出了万向锁的约束范围,则这两个关键帧之间的路径就会发生偏移,反映在角色动画上是旋转偏移,反映在镜头控制上就是镜头抖动. 要获得路径偏移的感性认识,可以参考这个视频:这个视频和我的描述有些不同,该视频使用一个称为万向节的奇怪装置解释的,而我是直接用的物体坐标系但路径偏移都是一样的. http://v.youku.com/v_show/id_XNzkyOTIyMTI=.html 怎样解决万向锁这个问题? 出现这个问题的

了解SQL Server锁争用:NOLOCK 和 ROWLOCK 的秘密

关系型数据库,如SQL Server,使用锁来避免多用户修改数据时的并发冲突.当一组数据被某个用户锁定时,除非第一个用户结束修改并释放锁,否则其他用户就无法修改该组数据. 有些数据库,包括SQL Server,用锁来避免用户检索未递交的修改记录.在这些系统中,如果用户A在修改一组记录,则其他用户只有等用户A修改完毕了,才能检索. 数据库在每个物理层上设置锁:记录行(rows),数据页(pages, 上百万记录行),扩展页(extends, 多个数据页),整个表,甚至整个数据库.有些数据库(如Or

航天七三一医院护理电子病历的设计与实施

中图分类号:TP3                                                         论文编号: 专业硕士学位论文   航天七三一医院护理电 子病历的设计与实施 作者姓名 学科专业  软件工程 指导教师 培养院系  软件学院 The Design and Implementation of Aerospace 731 hospital electronic nursing record system   A Dissertation Submitte