同步代码块

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. 框架:Intellij搭建Spring框架

    第二章.Intellij搭建Spring框架 前提条件:jdk.jre已经安装完成 方法一.intellij下载jar 附:自带的jar的版本为4.3[2018/11/22] 第一步:选择File&g ...

  2. Java学习笔记22(List接口)

    List接口继承自Collection接口,自身具有三大特点: 1.有序集合:存入和取出的顺序一致: 2.此接口的用户可以对每个元素插入位置进行精确控制:可以通过索引操作元素 3.可以存储重复元素 L ...

  3. Android直播实现srs流媒体服务器部署

    链接:http://blog.csdn.net/mr_sk/article/details/71086885 在配置文件中加入 pid 参数执行

  4. POJ2777-Count Color (线段树)

    题目传送门:http://poj.org/problem?id=2777 Count Color Time Limit: 1000MS   Memory Limit: 65536K Total Sub ...

  5. unity 实现调用Windows窗口/对话框交互

    Unity调用Window窗口 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分 ...

  6. Vue.js学习使用心得(一)

    一.初步了解 Vue.js是一套构建用户界面的渐进式框架,它只关注视图层, 采用自底向上增量开发的设计. Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件,相当于JavaS ...

  7. 多行文本用textarea而不是input type=textarea“”

    <textarea name="zhaiyao" id="" cols="35" rows="4">< ...

  8. 使用w uptime vmstat top sar nload 等命令查看系统负载

    1.  w 和uptime,查看cpu的使用率: 2.vmstat 命令,查看更细的物理设备使用状况: 3.top 命令: top -c  可具体查看命令及路径: top -bn1 静太显示一条命令, ...

  9. U盘支持启动windows和Linux

    1.我是利用大白菜先按照普通方法制作可以引导windows的启动盘,这个没什么好说了,就不多说了. 2.在U盘的根目录下添加一个放Ubuntu镜像的文件夹,然后将Ubuntu的iso文件拷贝至这个目录 ...

  10. js添加select中option

    1.js代码 $("#year").append("<option value="+value的值+">"+内容+"& ...