JAVA多线程(2)——锁(对象锁和类锁)
1、如下代码
1 public class TestSync1 implements Runnable {
2 Timer1 timer = new Timer1();
3 public static void main(String[] args) {
4 TestSync1 test = new TestSync1();
5 Thread t1 = new Thread(test);
6 Thread t2 = new Thread(test);
7 t1.setName("t1");
8 t2.setName("t2");
9 t1.start();
10 t2.start();
11 }
12
13 public void run() {
14 timer.add(Thread.currentThread().getName());
15 }
16
17 }
18
19 class Timer1 {
20 private static int num = 0;
21 //int num = 0;
22 public void add(String name) {
23 //synchronized(this) { //锁定当前对象:执行大括号之间语句的过程中,一个线程执行的过程中,不会被另一个线程打断;锁定当前对象,当然成员变量也被锁定了
24 num++; //这一段的执行过程被打断了
25 try {
26 Thread.sleep(1000);
27 } catch (InterruptedException e) {
28 e.printStackTrace();
29 }
30 System.out.println(name + ", 你是第" + num + "个使用timer的线程");
31 //}
32 }
结果:
D:\聚划算\技术部\编程练习\TestThread\Sync>java TestSync1
t1, 你是第2个使用timer的线程
t2, 你是第2个使用timer的线程
2、解决:加锁
1 public class TestSync1 implements Runnable {
2 Timer1 timer = new Timer1();
3 public static void main(String[] args) {
4 TestSync1 test = new TestSync1();
5 Thread t1 = new Thread(test);
6 Thread t2 = new Thread(test);
7 t1.setName("t1");
8 t2.setName("t2");
9 t1.start();
10 t2.start();
11 }
12
13 public void run() {
14 timer.add(Thread.currentThread().getName());
15 }
16
17 }
18
19 class Timer1 {
20 private static int num = 0;
21 //int num = 0;
22 public void add(String name) {
23 synchronized(this) { //锁定当前对象:执行大括号之间语句的过程中,一个线程执行的过程中,不会被另一个线程打断;锁定当前对象,当然成员变量也被锁定了
24 num++;
25 try {
26 Thread.sleep(1000);
27 } catch (InterruptedException e) {
28 e.printStackTrace();
29 }
30 System.out.println(name + ", 你是第" + num + "个使用timer的线程");
31 }
32 }
结果:
D:\聚划算\技术部\编程练习\TestThread\Sync>java TestSync1
t2, 你是第1个使用timer的线程
t1, 你是第2个使用timer的线程
分析一下1、2中的内存情况:只有一个test实例,该实例包含一个timer实例变量,因此代码中的private static int num = 0;换成int num = 0;对结果没有任何影响
接着,我们考虑如下这段代码,即在上述例子基础上新增了一个test实例,即共有test1、test2两个实例:
1 public class TestSync1 implements Runnable {
2 Timer1 timer = new Timer1();
3 public static void main(String[] args) {
4 TestSync1 test1 = new TestSync1();
5 TestSync1 test2 = new TestSync1();
6 Thread t1 = new Thread(test1);
7 Thread t2 = new Thread(test2);
8
9 t1.setName("t1");
10 t2.setName("t2");
11 t1.start();
12 t2.start();
13 }
14
15 public void run() {
16 timer.add(Thread.currentThread().getName());
17 }
18
19 }
20
21 class Timer1 {
22 //private static int num = 0;
23 int num = 0;
24 public void add(String name) {
25 synchronized(this) { //锁定当前对象:执行大括号之间语句的过程中,一个线程执行的过程中,不会被另一个线程打断;锁定当前对象,当然成员变量也被锁定了
26 num++; //这一段的执行过程被打断了
27 try {
28 Thread.sleep(3000);
29 } catch (InterruptedException e) {
30 e.printStackTrace();
31 }
32 System.out.println(name + ", 你是第" + num + "个使用timer的线程");
33 }
34 }
结果:
D:\聚划算\技术部\编程练习\TestThread\Sync>java TestSync1
t1, 你是第1个使用timer的线程
t2, 你是第1个使用timer的线程
分析一下内存使用情况:有两个test实例:test1、test2,它们分别各自指向自己的实例变量timer
如果第23行改为,private static int num = 0;,则执行结果为
D:\聚划算\技术部\编程练习\TestThread\Sync>java Test
t1, 你是第2个使用timer的线程
t2, 你是第2个使用timer的线程
这是因为此时num为Timer的类变量,为所有实例所共有
简单改造一下,把Timer1 这个类相关的方法执行放到run方法里面,如下:
1 public class TestSyncNew implements Runnable {
2 int num = 0;
3
4 public static void main(String[] args) {
5 TestSyncNew test1 = new TestSyncNew();
6 //TestSyncNew test2 = new TestSyncNew();
7 Thread t1 = new Thread(test1);
8 Thread t2 = new Thread(test1);
9 //Thread t2 = new Thread(test2);
10
11 t1.setName("t1");
12 t2.setName("t2");
13 t1.start();
14 t2.start();
15 }
16
17 public void run() {
18 synchronized(this) { //锁定当前对象:执行大括号之间语句的过程中,一个线程执行的过程中,不会被另一个线程打断;锁定当前对象,当然成员变量也被锁定了
19 num++; //这一段的执行过程被打断了
20 try {
21 Thread.sleep(1000);
22 } catch (InterruptedException e) {
23 e.printStackTrace();
24 }
25 System.out.println(Thread.currentThread().getName() + ", 你是第" + num + "个使用timer的线程");
26 }
27 }
28 }
结果为:
D:\聚划算\技术部\编程练习\TestThread\Sync>java TestSyncNew
t1, 你是第1个使用timer的线程
t2, 你是第2个使用timer的线程
1 public class TestSyncNew implements Runnable {
2 int num = 0;
3
4 public static void main(String[] args) {
5 TestSyncNew test1 = new TestSyncNew();
6 TestSyncNew test2 = new TestSyncNew();
7 Thread t1 = new Thread(test1);
8 //Thread t2 = new Thread(test1);
9 Thread t2 = new Thread(test2);
10
11 t1.setName("t1");
12 t2.setName("t2");
13 t1.start();
14 t2.start();
15 }
16
17 public void run() {
18 synchronized(this) { //锁定当前对象:执行大括号之间语句的过程中,一个线程执行的过程中,不会被另一个线程打断;锁定当前对象,当然成员变量也被锁定了
19 num++; //这一段的执行过程被打断了
20 try {
21 Thread.sleep(1000);
22 } catch (InterruptedException e) {
23 e.printStackTrace();
24 }
25 System.out.println(Thread.currentThread().getName() + ", 你是第" + num + "个使用timer的线程");
26 }
27 }
28 }
结果为:
D:\聚划算\技术部\编程练习\TestThread\Sync>java TestSyncNew
t1, 你是第1个使用timer的线程
t2, 你是第1个使用timer的线程
如上的两个例子内存空间分别如下的左右两边所示,例子1
解释结果:参考了http://jasshine.iteye.com/blog/1617813
例子1:
因为加了synchronzied,实现了同步,并且该对象锁对应的对象只有一个,那就是test1,所以当第一个线程锁住了test1,而第二个线程里面也是通过test1去访问run()方法,所以必须等第一个线程执行完对象的方法时才能获得对象锁。因此必须隔1秒钟才能执行当前线程
例子2:
因为此时每个线程都是通过不同的对象去访问run()方法,一个为test1,另外一个为test2,所以有2把对象锁,这2个对象锁毫不干,第一个线程锁住了test1,而第二个线程都是通过
test2对象去访问的,所以仍然能访问该方法。
类锁:
1 class TestSynchronized extends Thread {
2 public TestSynchronized(String name) {
3 super(name);
4 }
5
6 public synchronized static void prt() {
7 for (int i = 10; i < 20; i++) {
8 System.out.println(Thread.currentThread().getName() + " : " + i);
9 try {
10 Thread.sleep(100);
11 } catch (InterruptedException e) {
12 System.out.println("Interrupted");
13 }
14 }
15 }
16
17 public synchronized void run() {
18 System.out.println(Thread.currentThread().getName() + " in here");
19 /*
20 for (int i = 0; i < 10; i++) {
21 System.out.println(Thread.currentThread().getName() + " : " + i);
22 try {
23 Thread.sleep(100);
24 } catch (InterruptedException e) {
25 System.out.println("Interrupted");
26 }
27 } */
28 }
29 }
30
31 public class TestThread {
32 public static void main(String[] args) {
33 TestSynchronized t1 = new TestSynchronized("t1");
34 TestSynchronized t2 = new TestSynchronized("t2");
35 t1.start();
36
37 t1.prt();// (1)
38 t2.prt();// (2)
39
40 }
41 }
在代码(1)中,虽然是通过对象t1来调用prt()函数的,但由于prt()是静态的,所以调用它时不用经过任何对象,它所属的线程为main线程。
由于调用run()函数取走的是对象锁,而调用prt()函数取走的是class锁,所以同一个线程t1(由上面可知实际上是不同线程)调用run()函数且还没完成run()函数时,它就能调用prt()函数。但prt()函数只能被一个线程调用,如代码(1)和代码(2),即使是两个不同的对象也不能同时调用prt()。
结果:
D:\聚划算\技术部\编程练习\TestThread\Sync>java TestThread
main : 10
t1 in here //如果注释掉35行 t1.start();,则该行不打印
main : 11
main : 12
main : 13
main : 14
main : 15
main : 16
main : 17
main : 18
main : 19
main : 10
main : 11
main : 12
main : 13
main : 14
main : 15
main : 16
main : 17
main : 18
main : 19
上面的一个程序足以说明同步方法、对象锁和类锁的概念了。总结一下:
1. java中的每个对象都有一个锁,当访问某个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法在去访问该syncronized 方法了,直到之前的那个线程执行方法完毕后,其他线程才有可能去访问该synchronized方法。
2.如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到某个synchronzed方法,那么在该方法没有执行完毕前,其他线程无法访问该对象的任何synchronzied 方法的,但可以访问非synchronzied方法。
3.如果synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchuronized方法所在对象的对应的Class对象(类锁),
因为java中无论一个类有多少个对象,这些对象会对应唯一一个Class 对象,因此当线程分别访问同一个类的两个对象的static,synchronized方法时,他们的执行也是按顺序来的,也就是说一个线程先执行,一个线程后执行。
JAVA多线程(2)——锁(对象锁和类锁)的更多相关文章
- 编程开发之--java多线程学习总结(3)类锁
2.使用方法同步 package com.lfy.ThreadsSynchronize; /** * 1.使用同步方法 * 语法:即用 synchronized 关键字修饰方法(注意是在1个对象中用锁 ...
- “全栈2019”Java多线程第三十章:尝试获取锁tryLock()方法详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- 从火箭发场景来学习Java多线程并发闭锁对象
从火箭发场景来学习Java多线程并发闭锁对象 倒计时器场景 在我们开发过程中,有时候会使用到倒计时计数器.最简单的是:int size = 5; 执行后,size—这种方式来实现.但是在多线程并发的情 ...
- Java基础-IO流对象之字符类(FileWrite与FileReader)
Java基础-IO流对象之字符类(FileWrite与FileReader) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.常见编码简介 1>ASCII 我们知道计算机是 ...
- Java基础-IO流对象之File类
Java基础-IO流对象之File类 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.IO技术概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下 ...
- Java多线程操作同一个对象,线程不安全
Java多线程操作同一个对象 发现问题:多个线程操作同一资源的情况下,线程不安全,数据紊乱 代码: package multithreading; // Java多线程操作同一个对象 // 买火车票的 ...
- Java多线程之释放对象的锁
由于等待一个锁定线程只有在获得这把锁之后,才能恢复运行,所以让持有锁的线程在不需要锁的时候及时释放锁是很重要的.在以下情况下,持有锁的线程会释放锁: 1. 执行完同步代码块. 2. 在执行 ...
- Java 多线程之哪个对象才是锁?
问题背景 在感觉正常的使用ArrayList的迭代删除的操作的时候,发现了如下的崩溃日志: Caused by: java.util.ConcurrentModificationException a ...
- java的synchronized有没有同步的类锁?
转自:http://langgufu.iteye.com/blog/2152608 http://www.cnblogs.com/beiyetengqing/p/6213437.html 没有... ...
- java 多线程:Callable接口;FutureTask类实现对象【Thread、Runnable、Callable三种方式实现多线程的区别】
Callable接口介绍: Java5开始,Java提供了Callable接口,像是Runnable接口的增强版,Callable接口提供了一个 call()方法可以作为线执行体. call()方法比 ...
随机推荐
- Vue报错: Property or method "changeLoginType" is not defined on the instance but referenced during render
原因 我这里是因为我代码中的方法不存在,我漏写了,后补充上就好了 解决方案 在methods中添加如下代码: // 修改登录状态 changeLoginType(bool){ this.loginTy ...
- Numpy,一篇足以
numpy 用于数值计算 ndarray, 一个有效的多维数组,能提供以数组为导向的快速数值计算和灵活的广播功能(broadcasting) 便利的数学函数 用于读取/写入(reading/writi ...
- 【pandas小技巧】--缺失值的列
在实际应用中,数据集中经常会存在缺失值,也就是某些数据项的值并未填充或者填充不完整.缺失值的存在可能会对后续的数据分析和建模产生影响,因此需要进行处理. pandas提供了多种方法来处理缺失值,例如删 ...
- 基于Prometheus搭建监控平台
目录 前言 配置server单节点 prometheus.service 配置node节点 配置mysql监控 在数据库中添加exporter账户 修改mysql_exporter的配置 添加serv ...
- WPF实现类似ChatGPT的逐字打印效果
背景 前一段时间ChatGPT类的应用十分火爆,这类应用在回答用户的问题时逐字打印输出,像极了真人打字回复消息.出于对这个效果的兴趣,决定用WPF模拟这个效果. 真实的ChatGPT逐字输出效果涉及其 ...
- 极简工作流「GitHub 热点速览」
原以为 LLM 很难,但其实可以很简单,比如 Flowise 拖拽拖拽就能出来一个 LLM 流程,非常简单你的 LLM 就可以 run 起来了.同样的 web-check 也能极快速地帮你解决 Web ...
- [glibc2.23源码]阅读源码&调试,找出free_hook-0x13分配失败的原因
0x00 写在前面 发freebuf了:https://www.freebuf.com/articles/endpoint/373258.html 本次阅读源码是本人第一次,算是一个全新的开始.本次看 ...
- 实现在Qt窗口中嵌套SDL
实现在Qt窗口中嵌套SDL 在现代软件开发中,多媒体处理和交互性成为应用程序不可或缺的一部分.Qt作为一个强大的GUI框架,为开发者提供了丰富的图形.界面和事件处理工具.然而,有时候,我们可能需要更多 ...
- 「学习笔记」扩展 KMP(Z 函数)
对于个长度为 \(n\) 的字符串 \(s\).定义 \(z[i]\) 表示 \(s\) 和 \(s[i,n-1]\)(即以 \(s[i]\) 开头的后缀)的最长公共前缀(LCP)的长度.\(z\) ...
- java与es8实战之二:实战前的准备工作
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇是<java与es8实战>系 ...