java 同步代码块与同步方法

时间:2023-01-21 20:33:31

同步代码块

synchronized (obj) {
// 代码块
}

obj 为同步监视器,以上代码的含义为:线程开始执行同步代码块(中的代码)之前,必须先获得对同步监视器的锁定。

代码块中的代码是执行代码,即是某个方法中的某一部分代码,synchronized(obj){}只能出现在某个方法中。如:

    public void test() {

        synchronized (obj) {
// 代码块
}
}

而不能出现在其他位置,如下则报错:

public class Test {

    public void test(String[] strs) {

    }

// 报错,只能出现在某个方法中
synchronized (obj) {
}
}

同步代码块示例:

定义一个Account类,用于存储账户金额

class Account {

    // 账户余额
private double balance; public Account(double balance) {
this.balance = balance;
} // 设置余额
public void setBalance(double balance) {
this.balance = balance;
} // 取出余额
public double getBalance() {
return balance;
}
}

定义1个线程类用于对某个账户进行操作(取出账户中的余额),该线程类不包含同步代码块

class DrawMoney extends Thread {

    private Account account;  // 待取账户
private double amount; // 取出金额 public DrawMoney(Account account, double amount) {
this.account = account;
this.amount = amount;
} // 取出account中的余额,取出数量为amount
public void run() {
// 若account中的余额大于等于amount,取钱成功
if (amount <= account.getBalance()) {
System.out.println(getName() + " : " + "Get money:" + amount); // 线程休眠2毫秒
try {
Thread.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} account.setBalance(account.getBalance() - amount);
System.out.println(getName() + " : " + "Balance is " + account.getBalance());
}
else
System.out.println("The balance is not enough");
}
}

使用上述Account类及DrawMoney类定义一个账户,两个用户,两个用户同时对账户操作(取钱)。

public class SynchronizedTest {

    public static void main(String[] args) {
Account account = new Account(100);
DrawMoney user1 = new DrawMoney(account, 70);
DrawMoney user2 = new DrawMoney(account, 70);
user1.start();
user2.start();
}
}

运行结果:

Thread-0 : Get money:70.0
Thread-1 : Get money:70.0
Thread-0 : Balance is 30.0
Thread-1 : Balance is -40.0

由上可知,第二个用户取钱出现错误,余额不应当小于0。这是由于两个并发运行的线程(同时取钱的用户)同时对account操作,而不是一个取钱完成,再交给下一个。用户1还没来得及修改余额,用户2就开始取钱。

修改上述线程类,同步其中的取钱操作

class DrawCash extends Thread {

    private Account account;
private double amount; public DrawCash(Account account, double amount) {
this.account = account;
this.amount = amount;
} public void run() { // 使用account作为同步监视器,线程在执行下面的代码之前需要先锁定account synchronized (account) {
if (amount <= account.getBalance()) {
System.out.println(getName() + " : " + "Get money:" + amount);
try {
Thread.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
account.setBalance(account.getBalance() - amount);
System.out.println(getName() + " : " + "Balance is " + account.getBalance());
}
else
System.out.println(getName() + " : " + "The balance is not enough");
}
}
}

这时

public class SynchronizedTest {

    public static void main(String[] args) {
Account account = new Account(100);
DrawCash user1 = new DrawCash(account, 70);
DrawCash user2 = new DrawCash(account, 70);
user1.start();
user2.start();
}
}

运行结果:

Thread-0 : Get money:70.0
Thread-0 : Balance is 30.0
Thread-1 : The balance is not enough

第一个线程执行同步代码块时锁定监视器account,第二个线程执行同步代码块时也需要锁定监视器account,

但此时account被线程0锁定,故线程1只有在线程0的同步代码块执行完毕后才能执行其同步代码块。

使用DrawMoney与DrawCash各定义一个用户,对同一个账户取钱。

public class SynchronizedTest {

    public static void main(String[] args) {
Account account = new Account(100);
DrawCash user1 = new DrawCash(account, 70);
DrawMoney user2 = new DrawMoney(account, 70);
user1.start();
user2.start();
}
}

运行结果:

Thread-0 : Get money:70.0
Thread-1 : Get money:70.0
Thread-0 : Balance is 30.0
Thread-1 : Balance is -40.0

结果依旧出错,这是由于线程0需要锁定监视器account,但线程1不需要,故该情况下account的访问仍会出现线程不安全。

同步方法

被synchronized修饰的方法为同步方法,同步方法的同步监视器为this,即与该方法对应的对象(该方法所在的类生成的对象)。

    public synchronized void draw() {

    }

某个线程若要调用draw()方法,需要先锁定draw()对应的对象。

修改Account类,添加同步方法

class Account {

    // 账户余额
private double balance; public Account(double balance) {
this.balance = balance;
}
public synchronized void draw(double amount) {
if (amount > balance)
System.out.println(Thread.currentThread().getName() + " : " + "Balance is not enough");
else {
System.out.println(Thread.currentThread().getName() + " : " + amount);
balance -= amount;
System.out.println(Thread.currentThread().getName() + " : " + "Balance is " + balance);
}
} }

修改DrawMoney类

class DrawMoney extends Thread {

    private Account account;  // 待取账户
private double amount; // 取出金额 public DrawMoney(Account account, double amount) {
this.account = account;
this.amount = amount;
} // 取出account中的余额,取出数量为amount
public void run() {
account.draw(amount);
}
}

这时

public class SynchronizedTest {

    public static void main(String[] args) {
Account account = new Account(100);
DrawMoney user1 = new DrawMoney(account, 70);
DrawMoney user2 = new DrawMoney(account, 70);
user1.start();
user2.start();
}
}

运行结果:

Thread-0 : 70.0
Thread-0 : Balance is 30.0
Thread-1 : Balance is not enough

可见,线程是安全的

线程0调用draw()方法时锁定监视器account,1线程调用draw()时也需要锁定监视器account,

但此时account被线程0锁定,故线程1只有在线程0的调用完毕后才能调用。

上述的同步方法也可以用同步代码块实现:

class Account {

    // 账户余额
private double balance; public Account(double balance) {
this.balance = balance;
} public void draw(double amount) { synchronized (this) {
if (amount > balance)
System.out.println(Thread.currentThread().getName() + " : " + "Balance is not enough");
else {
System.out.println(Thread.currentThread().getName() + " : " + amount);
balance -= amount;
System.out.println(Thread.currentThread().getName() + " : " + "Balance is " + balance);
}
}
}
}

总结:

同步代码块与同步方法都是表明在执行某段代码前需要先锁定某个对象,同步代码块需指定,同步方法默认为this。

java 同步代码块与同步方法的更多相关文章

  1. java中的synchronized同步代码块和同步方法的区别

    下面这两段代码有什么区别? //下列两个方法有什么区别 public synchronized void method1(){} public void method2(){ synchronized ...

  2. Android(java)学习笔记68:同步代码块 和 同步方法 的应用

    1. 同步代码块 和 同步方法 代码示例: (1)目标类,如下: package cn.himi.text; public class SellTicket implements Runnable { ...

  3. Java的synchronized的同步代码块和同步方法的区别

    synchronized同步方法和同步代码块的区别 同步方法默认使用this或者当前类做为锁. 同步代码块可以选择以什么来加锁,比同步方法更精确,我们可以选择只有会在同步发生同步问题的代码加锁,而并不 ...

  4. Android(java)学习笔记8:同步代码块 和 同步方法 的应用

    1. 同步代码块 和 同步方法 代码示例: (1)目标类,如下: package cn.himi.text; public class SellTicket implements Runnable { ...

  5. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  6. java线程基础巩固---同步代码块以及同步方法之间的区别和关系

    在上一次中[http://www.cnblogs.com/webor2006/p/8040369.html]采用同步代码块的方式来实现对线程的同步,如下: 对于同步方法我想都知道,就是将同步关键字声明 ...

  7. 写2个线程,一个打印1-52,一个打印A-Z,打印顺序是12A34B。。。(采用同步代码块和同步方法两种同步方法)

    1.同步方法 package Synchronized; /************************************同步方法****************************** ...

  8. Java 同步代码块 - Synchronized Blocks

    java锁实现原理: http://blog.csdn.net/endlu/article/details/51249156 The synchronized keyword can be used ...

  9. Java:多线程,线程同步,synchronized关键字的用法(同步代码块、非静态同步方法、静态同步方法)

    关于线程的同步,可以使用synchronized关键字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象.本文探讨synchronized关键字. sy ...

随机推荐

  1. XmlBeanFactory的Bean加载

    如何使用这些bean,bean加载的探索: MyTestBean bean=(MyTestBean) bf.getBean("myTestBean"); AbstractBeanF ...

  2. Visual Studio Debugger AutoExp&period;dat &amp&semi; Visualization Framework

    bing.com搜索: autoexp.dat 参考资料: AutoExp.dat http://www.virtualdub.org/blog/pivot/entry.php?id=120 http ...

  3. Weka回归

    第一个数据挖掘技术:回归 例子:给房子定价 房子的价格(因变量)是很多自变量 — 房子的面积.占地的大小.厨房是否有花岗石以及卫生间是否刚重装过等的结果.所以,不管是购买过一个房子还是销售过一个房子, ...

  4. 字符编辑技术C语言实现

    #include<string.h> #include<ctype.h> #include<stdio.h> /*插入函数 ccode待插入的字符 anystrin ...

  5. Tomcat和JavaWeb目录和流程

    Tomcat主要目录结构 bin 二进制可执行文件,包含启动和关闭tomcat文件  conf 配置文件,其中包含了server.xml.context.xml.web.xml等  webapps 存 ...

  6. 超越村后端开发(5:远程同步本地与服务器端的MySQL数据库)

    1.同步MySQL数据库 服务器选用的华为云,安装了Ubuntu18.04,华为云默认是以root用户登录的. 1.使用Xshell6连接华为云 ls 2.Ubuntu18.04安装MySQL5.7 ...

  7. 2018 ACM 网络选拔赛 徐州赛区

    A. Hard to prepare #include <cstdio> #include <cstdlib> #include <cmath> #include ...

  8. JavaScript之this&comma;call&comma;apply

    this:被调用的上下文对象: apply与call:切换被调用的上下文对象,即 调用时,this被临时性地切换 //demo 1 [call] function forEach(list,callb ...

  9. 并发编程 —— Timer 源码分析

    前言 在平时的开发中,肯定需要使用定时任务,而 Java 1.3 版本提供了一个 java.util.Timer 定时任务类.今天一起来看看这个类. 1.API 介绍 Timer 相关的有 3 个类: ...

  10. UVALive 6906 Cluster Analysis 并查集

    Cluster Analysis 题目连接: https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemi ...