同步代码块

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. python selenium爬取QQ空间方法

    from selenium import webdriver import time # 打开浏览器 dr = webdriver.Chrome() # 打开某个网址 dr.get('https:// ...

  2. python flask大型项目目录

    Hello World 作者背景 应用程序简介 要求 安装 Flask 在 Flask 中的 “Hello, World” 下一步? 模板 回顾 为什么我们需要模板 模板从天而降 模板中控制语句 模板 ...

  3. mybatis Condition查询

    Condition condition = new Condition(ACurrentTotal.class); condition.createCriteria().andCondition(&q ...

  4. 使用Redis构建支持程序

    在Linux和Unix世界中,有两种常见的记录日志的方法.第一种是将日志记录到文件里面,然后随着时间流逝不断地将一个有一个日志行添加到文件里面,并在一段时间之后创建新的日志文件.包括Redis在内的很 ...

  5. 会话执行存储过程,等待被阻塞,Kill session场景模拟

    本次场景来源: 通知某个会话:执行execute addupp(1,'five');类似的存储过程,会话等待:(会话等待两种情况:一种确实执行,但是未完成:另一种就是执行的操作无法获取资源,等待资源释 ...

  6. jquery 获取访问当前页面的开源设备信息

    <!DOCTYPE html> <html> <head> <title>测试js</title> <meta http-equiv= ...

  7. Vue的新启之笔

    之前就有接触Vue这一语言,作为一个摊薄饼的我,觉得其基础性的知识体系与其他语言是相通的.且由于贵阳这一城市的地理位置的特殊性,我不得不承认想要从事软件开发这一行业,不精通一门语言不行.因为,任何一家 ...

  8. linux lamp编译环境安装

    apache 安装:http://blog.csdn.net/wplblog/article/details/52172128 编译安装 mysql安装:http://www.centoscn.com ...

  9. Python的方法和语法解释

    ---------------------------------------------------------------------------------------------------- ...

  10. Java 源码解析

    Object equals方法对比两个对象是否是内存中同一个物理地址 hashCode规定,当两个对象相等时,必须返回相等的hashCode,所以重写equals方法有必要重写hashCode方法 如 ...