java多线程的同步方法实例代码
作者:lqh 发布时间:2022-02-16 19:30:47
java多线程的同步方法实例代码
先看一个段有关银行存钱的代码:
class Bank {
private int sum;
public void add(int num){
sum = sum + num;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("total num is : " + sum);
}
}
class Custom implements Runnable{
private Bank b = new Bank();
@Override
public void run() {
for(int i = 3 ; i > 0 ; i--)
b.add(100);
}
}
public class BankDemo{
public static void main(String[] args) {
Custom custom = new Custom();
Thread t1 = new Thread(custom);
Thread t2 = new Thread(custom);
t1.start();
t2.start();
}
}
此代码的运行结果为:
total num is : 100
total num is : 300
total num is : 400
total num is : 500
total num is : 500
total num is : 600
可以看出sum的值与预期的效果不太一样;造成这种现象的原因有两个:
1.程序存在两个以上的子线程;
2.子线程中存在多条语句操作同一变量;
上述例子中:创建了两个子线程·t1 和 t2,分别向银行中存钱。但是可以看出银行的实力随着Custom的创建,只创建了一个对象。也就是说我们只操作一个数据变量即为银行中钱的总数sum;当两个子线程开启的时候run方法中调用了bank的add方法,而add方法中有两个语句都在操作sum一个sum的增加,一个是打印sum,当两个子线程抢占cpu执行各自的程序的时候会出现:
当t1执行到add以后,t2抢到了cpu的执行权,执行也是执行了add语句,随后打印出sum的值,这时候由于sum增加了两次,所以打印出来的sum值为200。类推,假如这个时候t1又抢回了cpu的执行权,因此又打印出一次200。
显然这种现象是我们不希望产生的。我们希望一个线程存完钱然后打印出结果,之后才允许下一次添加操作。这就是多线程会产生的问题,线程不安全。
我们应尽量避免这种现象的发生,Java给我们提供了三种方法来解决这个问题:
第一种:同步代码块
//private Object obj = new Object();
public void add(int num) {
synchronized (this) {
sum = sum + num;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("total num is : " + sum);
}
}
将多线程中需要操作同一数据对象的语句使用同步代码块包含。同步代码块的原理就是:
1.java中每个对象都有一个内置锁;
2.当程序运行到同步代码块的时候首先会获取指定对象的锁,这个锁对于多个线程来说是唯一的。我们可以创建任意一个对象(obj)让他当作同步代码块的锁。
3.当程序中只有一个只有一个锁的话我们还可以使用this,this代表当前执行代码所操作的实例对象的锁。即拥有add方法的类的对象,即bank。
4.两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
这样就可以操作同一个数据的多条语句只能在“同一段时间”只能被一个子线程所操作。
第二种 同步函数
public synchronized void add(int num) {
sum = sum + num;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("total num is : " + sum);
}
除了同步代码块以外我们还可以将需要同步的操作抽象成一个函数,然后将这个函数用synchronized修饰,形成同步方法。比如上述例子中的add方法中的语句都在操作sum对象。我们就可以将add方法使用synchronized修饰。这样也能达到代码同步的效果。
同步方法使用的锁其实就是 this。
值得一提的是:同步方法和同步代码块,在开发程序的时候我们更推荐使用同步代码块。
1.同步代码块可以绑定任意对象,而同步函数只能绑定该类对象this
2.如果多个线程使用同一个锁的话,那么两者均可以使用,如果存在多个锁的(比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁),只能使用同步代码块。
静态方法的同步
同步方法
public synchronized static void add(int num){}
同步代码块:
public synchronized void add(int num){
synchronized (Bank.Class) {
}
}
静态方法的默认同步锁是当前方法所在类的.class 对象,注意this与static不可以连用,所以不能使用this.Class
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!


猜你喜欢
- 1、添加依赖<dependency> <groupId>org.springframewo
- 本文介绍了Java 三种方式实现接口校验,主要包括AOP,MVC * ,分享给大家,具体如下:方法一:AOP代码如下定义一个权限注解pack
- BigDecimal 和 0 比较大小调用BigDecimal中的compareTo方法, 如:int i = bigDecimal.com
- 前言你一定会好奇:“老周,你去哪开飞机了?这么久没写博客了。”老周:“我买不起飞机,开了个铁矿,挖了一年半的石头。谁知铁矿垮了,压死了几条蜈
- C#实现委托namespace Delegate{ delegate void DGSayiHi(string n
- 本文实例讲述了Java实现的对称加密算法AES定义与用法。分享给大家供大家参考,具体如下:一 简介1、AES是目前使用最多的对称加密算法。2
- ①概念二叉搜索树又称二叉排序树,它或者是一棵空树**,或者是具有以下性质的二叉树:若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 双向顺序队列ArrayDeque和双向链式队列LinkedList,JDK已经包含,在此略。ArrayDeque包括顺序栈和顺序队列,Lin
- 本文实例讲述了Android编程判断是否连接网络的方法。分享给大家供大家参考,具体如下:判断wifi网络是否链接:public static
- 本文实例为大家分享了Android PickerScrollView滑动选择控件的具体使用代码,供大家参考,具体内容如下先看一下效果图1.S
- 引用Spring官方文档的说法介绍一下@Conditional注解:Spring5.0.15版本@Conditional注解官方文档@Con
- 一个简单好理解的例子,复制过去就能用,能看到效果首先对功能的思考,他怎么去实现1.制定udp广播的端口(如果收发用同一个端口就会一直接收到自
- 1、Spring的事务管理主要包括3个接口TransactionDefinition:封装事务的隔离级别,超时时间,是否为只读事务和事务的传
- 一、饿汉式单例类public class Singleton { privat
- 类加载器java中的类并不是一次加载完成的,而是按需加载。类加载器是用于加载java类到java虚拟机中的组件,它负责读取java字节码,并
- 前言一般生成的PDF文档默认的文档底色为白色,我们可以通过一定方法来更改文档的背景色,以达到文档美化以及保护双眼的作用。 以下内容提供了Ja
- 前言:事情是这样的:运营人员反馈,通过Excel导入数据时,有一部分成功了,有一部分未导入。初步猜测,是事务未生效导致的。查看代码,发现导入
- 在阅读这篇文章之前,大家可以先看下《Java多线程atomic包介绍及使用方法》,了解atomic包的相关内容。一、何谓Atomic?Ato
- 在混淆编译之前,我的程序可以正常运行,混淆编译时,报告如下错误: Error:Execution failed for task ‘:gvi
- 一、maven引入依赖,数据库驱动根据项目需求自行引入<!-- https://mvnrepository.com/artifact/