[笔记][Java7并发编程实战手册]2.2使用syncronized实现同步方法

学习多线程之前,我觉得很有必要去学习下

[笔记][思维导图]读深入理解JAVA内存模型整理的思维导图

基础知识

  1. 锁除了让临界区互斥执行外,

    还可以让释放锁的线程向获取同一个锁的线程发送消息

  2. 当线程获取锁时,JMM会把该线程对应的本地内存置为无效。

    从而使得监视器保护的临界区代码必须要从主内存中去读取共享变量。

  3. 当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。
  4. 加锁使用的是同一个锁的话,使用syncronized的静态方法和普通方法需要注意的是:静态方法:同时只能被一个线程访问,但是可以访问其他被syncronized修饰过的普通方法,所以:如果存在静态和非静态方法中都修改了同一个数据,那么就会出现并发问题

针对基础知识中的第4点验证

其他三点,请看java内存模型

例子:

public class SyncStatic {
    public static void main(String[] args) throws InterruptedException {
public class SyncStatic {
    public static void main(String[] args) throws InterruptedException {
        final A a = new A();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    a.addStatic();
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    a.sub();
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t2.start();
        t1.join();
        t2.join();
        System.out.println(a.num);

    }
}
class A{
    public static int num = 0;
    public static synchronized void addStatic(){
        ++num;
        System.out.println("addStatic:" + num);
    }

    /**
     * 想看到正确的结果,请把这里的static注释放开
     */
    public /*static*/ synchronized void sub(){
        --num;
        System.out.println("sub:" + num);
    }
}

某一次运行结果:

addStatic:0
sub:0
sub:0
addStatic:1
addStatic:1
sub:0
sub:-1
addStatic:0
sub:-1
addStatic:0
addStatic:1
sub:1
addStatic:1
sub:1
sub:0
addStatic:1
sub:1
addStatic:1
addStatic:2
sub:2
2

说明:

一个A类。两个线程:一个线程相加10次,一个线程相减10次,最后的结果肯定是不变的。但是这里实现正面变化了。所以说:要注意被syncronized修饰过的静态方法


充钱取钱的列子

**
 * Created by zhuqiang on 2015/8/5 0005.
 * 模拟账户
 */
public class Account {
    private String userName;  //用户
    private double money;  //余额

    public synchronized void add(double money){
        this.money = this.money + money;
        System.out.println(this.getUserName() + ",+100:余额:" + this.getMoney());
    }

    public synchronized void subtract(double money){
        this.money = this.money - money;
        System.out.println(this.getUserName()  + ",-100:余额:" + this.getMoney());
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}
/**
 * Created by zhuqiang on 2015/8/5 0005.
 * atm 机器,往里面不断的充值
 */
public class AtmAdd implements Runnable {
    private Account account;
    public AtmAdd(Account account){
        this.account = account;
    }
    @Override
    public void run() {
        for (int i = 1;i <=100;i++){
            account.add(100);
        }
    }
}
/**
 * Created by zhuqiang on 2015/8/5 0005.
 * atm 机器,往里面不断的扣钱
 */
public class AtmSub implements Runnable {
    private Account account;
    public AtmSub(Account account){
        this.account = account;
    }
    @Override
    public void run() {
        for (int i = 1;i <=100;i++){
            account.subtract(100);

        }
    }
}
/**
 * Created by zhuqiang on 2015/8/5 0005.
 */
public class Client {
    public static void main(String[] args) throws Exception {
        Account account = new Account();
        account.setMoney(0);
        account.setUserName("小明");
        Thread add = new Thread(new AtmAdd(account));
        add.start();
        Thread sub = new Thread(new AtmSub(account));
        sub.start();
        add.join();  //等待该线程终止,主线程才执行
        sub.join();
        System.out.println(account.getMoney());
    }
}

说明:

运行结果就不贴出了;

该示例有2个线程,一个线程不断的往账户中冲钱,一个线程不断的往账户中扣钱。正确同步的结果是最后账户中的钱是不变的。因为执行的次数都一样

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 14:15:01

[笔记][Java7并发编程实战手册]2.2使用syncronized实现同步方法的相关文章

[笔记][Java7并发编程实战手册]系列目录

Java7并发编程实战手册 这一本实战的书籍.本笔记记录是看了该书.并且简化了书中的示例.的一些随笔记录 我觉得能给我更好的感觉.我觉得先看博客中转载的多线程系列 Java多线程系列-目录源码分析和理论.有时候真的觉得好烦躁.可是,没有这些理论实战中又觉得太多的未知. 所以本人觉得.先粗略的过一遍理论和源码分析.再来看学习实战,在写代码的过程中,去回想和联想理论就能更好的把知识串联起来了: 也可以看到本人在记录这些笔记的时候也会引用到博客中转载的多线程系列文章. [笔记][Java7并发编程实战

[笔记][Java7并发编程实战手册]3.2 资源的并发访问控制Semaphore信号量

[笔记][Java7并发编程实战手册]系列目录 简介 本文学习信号量Semaphore机制. Semaphore 本质是一个共享锁 内部维护一个可用的信号集,获取信号量之前需要先申请获取信号数量:用完之后,则需要释放信号量:如果不释放,那么其他等待线程则一直阻塞直到获取信号量或则被中断为止 本人的理解是:互斥锁是同一时间只能一个线程访问,而在这里,是同一时间允许获取到了信号量的线程并发访问,而没有获取到信号量的则必须等待信号量的释放: 将信号量初始化为 1,使得它在使用时最多只有一个可用的许可,

[笔记][Java7并发编程实战手册]4.3 创建固定的线程执行器newFixedThreadPool线程池

[笔记][Java7并发编程实战手册]系列目录 简介 newFixedThreadPool(int nThreads, ThreadFactory threadFactory) 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程,在需要时使用提供的 ThreadFactory 创建新线程. newCachedThreadPool()创建的线程池的特性是:自动回收不使用的线程(终止并从缓存中移除那些已有 60 秒钟未被使用的线程),(在无可用线程的情况下)自动的为新来的task创

[笔记][Java7并发编程实战手册]3.3 资源的多副本并发访问控制Semaphore

[笔记][Java7并发编程实战手册]系列目录 简介 本文继续学习信号量Semaphore机制. 在3.2中其实已经讲解完了,之前对于信号量并发的使用场景不知道,看了本章节才想到一些: 下面就以 租车为列子来讲解并发访问的控制.(示例都很简单或许不符合现实逻辑) 信号量(非二进制信号量)是不保证同步的,需要额外的同步 示例 场景:有一个出租车公司,有三台车,有十个司机,每个司机工作的时间不一致,可以说是司机等待着别人还车后,接着租用汽车. /** * Created by zhuqiang on

[笔记][Java7并发编程实战手册]2.5使用Lock实现同步二

[笔记][Java7并发编程实战手册]系列目录 概要 接上一篇文章,练习修改锁的公平性,和在所中使用条件. 修改锁的公平性ReentrantLock /** *构造一个锁对象,默认为非公平锁 */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } 根据ReentrantLock的构造可以看出来,默认会构造非公平锁: 公平锁与非公平锁有什么区别 公平锁 :有多个线程并发访

[笔记][Java7并发编程实战手册]第三章-线程同步辅助类-概要

[笔记][Java7并发编程实战手册]系列目录 有点着急了,没有太注重质量,自己也没有理解透,从本章起,读书和随笔笔记的质量会更好. 第三章 在本章中,我们将学习: 资源的并发访问控制 资源的多副本的并发访问控制 等待多个并发事件的完成 在集合点的同步 并发阶段任务的运行 并发阶段任务中的阶段交换 并发任务间的数据交换 回顾 在第二章中主要学习了以下接口 synchronized关键字 Lock接口以及实现类,如ReentrantLock.ReentrantReadWriteLock中的Read

[笔记][Java7并发编程实战手册]4.4 在执行器中执行任务并返回结果Callable、Future

[笔记][Java7并发编程实战手册]系列目录 简介 执行框架(Executor Framework)的优势之一就是,可以在运行并发任务的时候返回结果.但是需要以下两个类来实现功能: 1. 接口 Callable<V> 返回结果并且可能抛出异常的任务.实现者定义了一个不带任何参数的叫做 call 的方法. Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的.但是 Runnable 不会返回结果,并且无法抛出经过检查的异常. Executors 类

[笔记][Java7并发编程实战手册]4.5 运行多个任务并处理第一个结果ThreadPoolExecutor

[笔记][Java7并发编程实战手册]系列目录 简介 看到这个标题的时候,我也很纳闷,因为没有明白所表达的意思. ok,并发编程比较常见的一个问题是:当采用多个并发任务来解决一个问题的时候,往往只需要关心这个任务的第一个结果,例如:验证一个算法的时候,假如一个执行5个算法,那么最先返回结果的,就是最快的. 在本章将会学习,如何使用ThreadPoolExecutor来实现类似场景: 本章ThreadPoolExecutor使用心得 使用 ThreadPoolExecutor.invokeAny(

[笔记][Java7并发编程实战手册]2.4在同步代码中使用条件-生产者与消费者

说明 在并发编程中一个典型的问题是生产者–消费者问题.在程序中,有可能会需要用到两个线程通信的情况,比如生产者消费者中,获取一个共享数据,有就消费.没有就等待着生产者生产之后再继续消费.那么这个实现过程就可以使用wait();notify();notifyAll()来达到效果: 以上方法详细解说请查看: Java多线程系列–"基础篇"05之 线程等待与唤醒 例子 /** * Created by zhuqiang on 2015/8/8 0008. */ public class Cl