文档章节

Java并发编程初级篇(十):synchronized同步方法

阿拉德大陆的魔法师
 阿拉德大陆的魔法师
发布于 2016/11/24 11:24
字数 863
阅读 333
收藏 0

#程序员薪资揭榜#你做程序员几年了?月薪多少?发量还在么?>>>

在Java中我们可以使用synchronized关键字来控制一段代码的并发访问。使用synchronized关键字修饰的代码在同一时间只有一个线程可以访问,其他要访问这个代码块的线程将被挂起。

使用synchronized关键字可以控制并发访问同时修改一份数据造成数据不一致的问题,但是synchronized会降低系统性能,因为即使你启动再多的线程,同一时间还是只有一个线程能访问,其他线程都在挂起,所以使用了synchronized也就没有真正意义上的并发了。

我们可以使用synchronized关键字修饰方法,那么这个方法就是同步方法。我们也可以使用synchronized来修饰代码款,那么这个代码块就是同步代码块。

示例代码

下面我们看一个经典的银行账号的例子,在这个例子中我们模拟一个银行账号的存取款过程,同时开启多个线程模拟多次并发的存取款操作,最后我们来看下账户余额。

创建Account类来模拟银行账户,其中修改账户金额的方法使用synchronized关键字修饰,打印账户变动并模拟每次账户修改操作需要1秒钟完成。

public class Account {
    private int amount;

    public Account(int amount) {
        this.amount = amount;
    }

    public int getAmount() {
        return amount;
    }

    public void setAmount(int amount) {
        this.amount = amount;
    }

    //public int modify(int amount) {
    public synchronized int modify(int amount) {
        if (amount > 0) {
            System.out.printf("%s: 向账户存款%d元.\n", Thread.currentThread().getName(), amount);
        } else if (amount < 0 && this.amount >= -amount) {
            System.out.printf("%s: 余额充足,从账户取款%d元.\n", Thread.currentThread().getName(), amount);
        } else if (amount < 0 && this.amount < -amount) {
            System.out.printf("%s: 余额不足,取款失败.\n", Thread.currentThread().getName());
            return this.amount;
        } else {
            return this.amount;
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        this.amount = this.amount + amount;

        return amount;
    }
}

新建线程类模拟用户的存取款操作

public class MyRunnable implements Runnable{
    private Account account;
    private int amount;

    public MyRunnable(Account account, int amount) {
        this.account = account;
        this.amount = amount;
    }

    @Override
    public void run() {
        account.modify(amount);
    }
}

最后再主方法类中,新建一个余额为1000元的账户,然后使用4个线程分别模拟1次存款操作,3次取款操作,正常情况应该是有一次取款失败,因为最后账户余额已经不够1000元了。

public class Main {
    public static void main(String[] args) {
        Account account = new Account(1000);

        Thread[] threads = new Thread[4];
        threads[0] = new Thread(new MyRunnable(account, 1000));
        threads[1] = new Thread(new MyRunnable(account, -1000));
        threads[2] = new Thread(new MyRunnable(account, -1000));
        threads[3] = new Thread(new MyRunnable(account, -1000));

        try {
            for (int i = 0; i < 4; i++) {
                threads[i].start();
                Thread.sleep(50);
            }

            for (int i = 0; i < 4; i++) {
                 threads[i].join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.printf("Main: 账户余额:%d\n", account.getAmount());
    }
}

查看日志,我们发现有一次取款失败,并且最后账户余额为0.

Thread-0: 向账户存款1000元.
Thread-3: 余额充足,从账户取款-1000元.
Thread-2: 余额充足,从账户取款-1000元.
Thread-1: 余额不足,取款失败.
Main: 账户余额:0

 我们如果去掉synchronized关键字修饰,你会发现三次取款都成功了,并且最后账户余额为-1000元。

Thread-0: 向账户存款1000元.
Thread-1: 余额充足,从账户取款-1000元.
Thread-2: 余额充足,从账户取款-1000元.
Thread-3: 余额充足,从账户取款-1000元.
Main: 账户余额:-1000

 

© 著作权归作者所有

阿拉德大陆的魔法师
粉丝 27
博文 91
码字总数 83019
作品 0
西城
程序员
私信 提问
加载中

评论(0)

求你了,再问你Java内存模型的时候别再给我讲堆栈方法区了…

GitHub 4.1k Star 的Java工程师成神之路 ,不来了解一下吗? GitHub 4.1k Star 的Java工程师成神之路 ,真的不来了解一下吗? GitHub 4.1k Star 的Java工程师成神之路 ,真的确定不来了解一下吗...

Hollis
2019/07/02
0
0
【原创】Java并发编程系列07 | synchronized原理

  20大进阶架构专题每日送达      并发编程中用到最多的关键字毫无疑问是synchronized。这篇文章就来探究下synchronized:   synchronized如何使用?   synchronized是实现同步加锁...

java进阶架构师
2019/11/13
0
0
跳槽时,这些Java面试题99%会被问到

我在 Oracle 已经工作了近 7 年,面试过从初级到非常资深的Java工程师,且由于 Java 组工作任务的特点,我非常注重面试者的计算机科学基础和编程语言的理解深度,可以不要求面试者非要精通 ...

Java小铺
2018/08/15
554
0
Java 编程之美:并发编程基础晋级篇

本文来自作者 加多 在 GitChat 上分享 「Java 并发编程之美:并发编程基础晋级篇」 编辑 | Mc Jin 借用 Java 并发编程实践中的话,编写正确的程序并不容易,而编写正常的并发程序就更难了! ...

gitchat
2018/04/18
0
0
关于Java里面多线程同步的一些知识

# 关于Java里面多线程同步的一些知识 对于任何Java开发者来说多线程和同步是一个非常重要的话题。比较好的掌握同步和线程安全相关的知识将使得我们则更加有优势,同时这些知识并不是非常容易...

欧阳海阳
2018/07/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

QT 执行shell命令

(1)首先包含头文件: #include <QProcess> (2)执行shell命令: QProcess::execute("ls");

悲催的古灵武士
4分钟前
9
0
osgEarth使用笔记3——加载倾斜摄影数据

目录 1. 概述 2. 详论 2.1. 位置 2.2. 着色 2.3. 其他 3. 结果 4. 参考 1. 概述 我在《OSG加载倾斜摄影数据》这篇博文中论述了如何通过OSG生成一个整体的索引文件,通过这个索引文件来正确显...

osc_7oc4d1en
5分钟前
8
0
cesium加载gltf模型点击以及列表点击定位弹窗

前言 cesium 官网的api文档介绍地址cesium官网api,里面详细的介绍 cesium 各个类的介绍,还有就是在线例子:cesium 官网在线例子,这个也是学习 cesium 的好素材。 之前有部分订阅者咨询我,...

osc_cx8uhydz
6分钟前
8
0
思维导图软件如何插入图片?具体步骤?

学习思维导图制作的过程中,会遇到很多没有学过的知识,需要我们不断地去改进和学习,这样增强自己的学习能力,才能更好地掌握制图软件。以后帮助我们快速方便地完成制图,今天我们就要来看看...

深蓝月上
6分钟前
10
0
Notepad++ 列块模式编辑,替换换行符

一、列块模式编辑: 1、数据准备 2、按住 “Alt + 鼠标左键” 选择需要列块模式编辑的区域,可以看到多了一条竖线 3、之后批量可以添加,修改内容 二、替换换行符 上面说了列块模式的编辑,后...

osc_itgved4p
7分钟前
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部