Java多线程——线程八锁案例分析

摘要:本文主要学习了多线程并发中的一些案例。

部分内容来自以下博客:

https://blog.csdn.net/dyt443733328/article/details/80019352

多线程的八个案例

通过分析代码,推测打印结果,并运行代码进行验证。

1)两个线程调用同一个对象的两个同步方法

代码如下:

 public class Demo {
public static void main(String[] args) {
Number number = new Number(); new Thread(new Runnable() {
@Override
public void run() {
number.getOne();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
number.getTwo();
}
}).start();
}
} class Number {
public synchronized void getOne() {
System.out.println("one");
} public synchronized void getTwo() {
System.out.println("two");
}
}

运行结果如下:

 one
two

结果分析:

被synchronized修饰的方法,锁的对象是方法的调用者。因为两个方法的调用者是同一个,所以两个方法用的是同一个锁,先调用方法的先执行。

2)新增Thread.sleep()给某个方法

代码如下:

 public class Demo {
public static void main(String[] args) {
Number number = new Number(); new Thread(new Runnable() {
@Override
public void run() {
number.getOne();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
number.getTwo();
}
}).start();
}
} class Number {
public synchronized void getOne() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
} public synchronized void getTwo() {
System.out.println("two");
}
}

运行结果如下:

 // 等待一秒。
one
two

结果说明:

被synchronized修饰的方法,锁的对象是方法的调用者。因为两个方法的调用者是同一个,所以两个方法用的是同一个锁,先调用方法的先执行,第二个方法只有在第一个方法执行完释放锁之后才能执行。

3)新增一个线程调用新增的一个普通方法

代码如下:

 public class Demo {
public static void main(String[] args) {
Number number = new Number(); new Thread(new Runnable() {
@Override
public void run() {
number.getOne();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
number.getTwo();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
number.getThree();
}
}).start();
}
} class Number {
public synchronized void getOne() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
} public synchronized void getTwo() {
System.out.println("two");
} public void getThree() {
System.out.println("three");
}
}

运行结果如下:

 three
// 等待一秒。
one
two

结果说明:

新增的方法没有被synchronized修饰,不是同步方法,不受锁的影响,所以不需要等待。其他线程共用了一把锁,所以还需要等待。

4)两个线程调用两个对象的同步方法,其中一个方法有Thread.sleep()

代码如下:

 public class Demo {
public static void main(String[] args) {
Number numberOne = new Number();
Number numberTwo = new Number(); new Thread(new Runnable() {
@Override
public void run() {
numberOne.getOne();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
numberTwo.getTwo();
}
}).start();
}
} class Number {
public synchronized void getOne() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
} public synchronized void getTwo() {
System.out.println("two");
}
}

运行结果如下:

 two
// 等待一秒。
one

结果说明:

被synchronized修饰的方法,锁的对象是方法的调用者。因为用了两个对象调用各自的方法,所以两个方法的调用者不是同一个,所以两个方法用的不是同一个锁,后调用的方法不需要等待先调用的方法。

5)将有Thread.sleep()的方法设置为static方法,并且让两个线程用同一个对象调用两个方法

代码如下:

 public class Demo {
public static void main(String[] args) {
Number number = new Number(); new Thread(new Runnable() {
@Override
public void run() {
number.getOne();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
number.getTwo();
}
}).start();
}
} class Number {
public static synchronized void getOne() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
} public synchronized void getTwo() {
System.out.println("two");
}
}

运行结果如下:

 two
// 等待一秒。
one

结果说明:

被synchronized和static修饰的方法,锁的对象是类的class对象。仅仅被synchronized修饰的方法,锁的对象是方法的调用者。因为两个方法锁的对象不是同一个,所以两个方法用的不是同一个锁,后调用的方法不需要等待先调用的方法。

6)将两个方法均设置为static方法,并且让两个线程用同一个对象调用两个方法

代码如下:

 public class Demo {
public static void main(String[] args) {
Number number = new Number(); new Thread(new Runnable() {
@Override
public void run() {
number.getOne();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
number.getTwo();
}
}).start();
}
} class Number {
public static synchronized void getOne() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
} public static synchronized void getTwo() {
System.out.println("two");
}
}

运行结果如下:

 // 等待一秒。
one
two

结果说明:

被synchronized和static修饰的方法,锁的对象是类的class对象。因为两个同步方法都被static修饰了,所以两个方法用的是同一个锁,后调用的方法需要等待先调用的方法。

7)将两个方法中有Thread.sleep()的方法设置为static方法,另一个方法去掉static修饰,让两个线程用两个对象调用两个方法

代码如下:

 public class Demo {
public static void main(String[] args) {
Number numberOne = new Number();
Number numberTwo = new Number(); new Thread(new Runnable() {
@Override
public void run() {
numberOne.getOne();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
numberTwo.getTwo();
}
}).start();
}
} class Number {
public static synchronized void getOne() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
} public synchronized void getTwo() {
System.out.println("two");
}
}

运行结果如下:

 two
// 等待一秒。
one

结果说明:

被synchronized和static修饰的方法,锁的对象是类的class对象。仅仅被synchronized修饰的方法,锁的对象是方法的调用者。即便是用同一个对象调用两个方法,锁的对象也不是同一个,所以两个方法用的不是同一个锁,后调用的方法不需要等待先调用的方法。

8)将两个方法均设置为static方法,并且让两个线程用同一个对象调用两个方法

代码如下:

 public class Demo {
public static void main(String[] args) {
Number numberOne = new Number();
Number numberTwo = new Number(); new Thread(new Runnable() {
@Override
public void run() {
numberOne.getOne();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
numberTwo.getTwo();
}
}).start();
}
} class Number {
public static synchronized void getOne() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
} public static synchronized void getTwo() {
System.out.println("two");
}
}

运行结果如下:

 // 等待一秒。
one
two

结果说明:

被synchronized和static修饰的方法,锁的对象是类的class对象。因为两个同步方法都被static修饰了,即便用了两个不同的对象调用方法,两个方法用的还是同一个锁,后调用的方法需要等待先调用的方法。

总结

一个类里面如果有多个synchronized方法,在使用同一个对象调用的前提下,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其他的线程都只能等待,换句话说,某一时刻内,只能有唯一一个线程去访问这些synchronized方法。

锁的是当前对象this,被锁定后,其他线程都不能进入到当前对象的其他的synchronized方法。

加个普通方法后发现和同步锁无关。

换成静态同步方法后,情况又变化。

所有的非静态同步方法用的都是同一把锁:实例对象本身。

也就是说如果一个对象的非静态同步方法获取锁后,该对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是其他对象的非静态同步方法因为跟该对象的非静态同步方法用的是不同的锁,所以毋须等待该对象的非静态同步方法释放锁就可以获取他们自己的锁。

所有的静态同步方法用的也是同一把锁:类对象本身。

这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间不会有竞争条件。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个对象的静态同步方法,还是其他对象的静态同步方法,只要它们属于同一个类的对象,那么就需要等待当前正在执行的静态同步方法释放锁。

Java多线程——线程八锁案例分析的更多相关文章

  1. java多线程 -- 线程八锁

    一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些 ...

  2. java多线程----线程池源码分析

    http://www.cnblogs.com/skywang12345/p/3509954.html 线程池示例 在分析线程池之前,先看一个简单的线程池示例. 1 import java.util.c ...

  3. java多线程——线程池源码分析(一)

    本文首发于cdream的个人博客,点击获得更好的阅读体验! 欢迎转载,转载请注明出处. 通常应用多线程技术时,我们并不会直接创建一个线程,因为系统启动一个新线程的成本是比较高的,涉及与操作系统的交互, ...

  4. Java多线程-线程的锁总结

    一.多线程-同步函数的锁是this /*同步函数用的是哪一个锁呢?函数需要被对象调用.那么函数都有一个所属对象引用.就是this.所以同步函数使用的锁是this. 通过该程序进行验证. 使用两个线程来 ...

  5. Java多线程系列--“JUC锁”03之 公平锁(一)

    概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...

  6. Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例

    概要 本章介绍JUC包中的CyclicBarrier锁.内容包括:CyclicBarrier简介CyclicBarrier数据结构CyclicBarrier源码分析(基于JDK1.7.0_40)Cyc ...

  7. Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock

    本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...

  8. Java多线程系列--“JUC锁”01之 框架

    本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--“JUC锁”01之 框架02. Java多线程系列--“JUC锁”02之 互斥锁Reentrant ...

  9. Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例

    概要 前面对"独占锁"和"共享锁"有了个大致的了解:本章,我们对CountDownLatch进行学习.和ReadWriteLock.ReadLock一样,Cou ...

随机推荐

  1. BZOJ 2806 Luogu P4022 [CTSC2012]Cheat (广义后缀自动机、DP、二分、单调队列)

    题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=2806 (luogu) https://www.luogu.org/pro ...

  2. Codeforces Round #254 (Div. 1) C DZY Loves Colors

    http://codeforces.com/contest/444/problem/C 题意:给出一个数组,初始时每个值从1--n分别是1--n.  然后两种操作. 1:操作 a.b内的数字是a,b内 ...

  3. Ubuntu 16.04安装汇编编译器NASM

    NASM支持intel语法. 安装过程: 1.通过二进制包方式 下载: http://www.nasm.us/pub/nasm/releasebuilds/2.13/ 如果要下载其它版本可以把地址靠前 ...

  4. KonBoot – 只要5K映象文件轻易绕过您的WindowsXP/VISTA/7系统的密码

    http://pannisec.diandian.com/?tag=konBoot 这个东西是我昨晚点击了QQ弹窗的那个SOSO问题后,有人问我如何破解Windows登陆密码,且不可以用net命令创建 ...

  5. zoj 1671 Walking Ant

    Walking Ant Time Limit: 2 Seconds      Memory Limit: 65536 KB Ants are quite diligent. They sometime ...

  6. OC中APPDelegate[[UIApplication shareApplication]delegate]]Swift实现

    直接上代码: var myDelegate:AppDelegate? myDelegate = UIApplication.sharedApplication().delegate as? AppDe ...

  7. 架构师速成6.7-设计开发思路-uml

    uml是什么东西?统一建模语言.一门语言.是用来进行软件设计的一门语言. 事实上一门语言的诞生并不伟大,让大多数人都使用才足够伟大. uml就是一门伟大的语言.由于眼下软件设计的唯一语言就是它. UM ...

  8. 启动第二个Activity

    启动第二个Activity activity_main.xml文件: <? xml version="1.0" encoding="utf-8"?> ...

  9. 第16章 ASP.NET MVC 日志篇

    本章主要介绍MVC中内置的错误处理.日志以及用来提升性能的监控工具 一.错误处理 当该网站忙于处理HTTP请求时,很多内容都会出错.幸运的是,MVC让错误处理工作变得相对简单了很多,因为MVC应用是运 ...

  10. 常用样式制作思路 自定义按钮~自适应布局~常见bug seajs简记 初学者必知的HTML规范 不容忽略的——CSS规范

    常用样式制作思路   学习常用样式总结参考来自这里 带点文字链接列表利用:before实现 1 <!DOCTYPE html> 2 <html lang="en" ...