同步代码块

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. Arduino 串口输出LM35温度

    #include "stdlib.h" float temp = 0.0; float maxtemp = 0.0; float mintemp =100.0; // the se ...

  2. nodejs --- querystring模块.

    1. 序列化: querystring.stringify(对象, 参数2, 参数3) 第二个参数 设置 连接符   从 & 变为 , 第三个参数 设置 key  和value的中间符号,  ...

  3. dict的items()方法于iteritems()方法的不同

    items方法将所有的字典以列表方式返回,其中项在返回时没有特殊的顺序: iteritems方法有相似的作用,但是返回一个迭代器对象.

  4. web.config 冲突的解决办法 (主目录子目录分别帮定域名导至出现错误)

    IIS上在主站点下搭建虚拟目录后,子站点中的<system.web>节点与主站点的<system.web>冲突解决方法: 在主站点的<system.web>上一级添 ...

  5. day12作业答案

    2.1 # lst=['asdgg','as','drtysr'] # lst2=[i.upper() for i in lst if len(i) >3 ] # print(lst2) # 2 ...

  6. java问题排查工具之一板斧jstack——使用 jstack 定位 java进程CPU过高的问题

    jstack主要用来查看某个Java进程内的线程堆栈信息.语法格式如下: jstack [option] pid jstack [option] executable core jstack [opt ...

  7. 数据结构中的列表、元组、字典、集合 ,深浅copy

    数据结构:数据结构是计算机存储数据和组织数据的方式.数据结构是指相互之间存在一种或多种特定关系的数据元素的集合.在python中主要的数据类型统称为容器. 而序列(如列表.元组).映射(如字典).集合 ...

  8. 1.4 Chrome浏览器

    1.4 Chrome浏览器 前言selenium2启动Chrome浏览器是需要安装驱动包的,但是不同的Chrome浏览器版本号,对应的驱动文件版本号又不一样,如果版本号不匹配,是没法启动起来的. ## ...

  9. advanced ip scanner —— 局域网下 ip 及设备的扫描

    advanced ip scanner 下载地址:Advanced IP Scanner - Download Free Network Scanner. 用于扫描当前局域网下全部设备及其 ip,构建 ...

  10. java-为什么非静态内部类中不能有static修饰的属性,但却可以有final常量?

    - 例: public class Demo{ int x; class A{ static int a = 0; //这样写是不合法的. static final int b=0; //这样写是合法 ...