文档章节

第二章-对象及变量的并发访问-第二篇

简心
 简心
发布于 08/21 20:51
字数 1695
阅读 5
收藏 0

锁对象的改变

请阅读如下代码

public class MainClass {
    private String lock = "123";
    public void printStringB() {
        try {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName()+"begin " +System.currentTimeMillis());
                lock="456";
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName()+"end " +System.currentTimeMillis());
            }
        } catch ( InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

public class ThreadA extends Thread {
    private MainClass mainClass;

    public ThreadA(MainClass mainClass) {
        this.mainClass = mainClass;
    }

    @Override
    public void run() {
            mainClass.printStringB();
    }
}


public class ThreadB extends Thread {
    private MainClass mainClass;

    public ThreadB(MainClass mainClass) {
        this.mainClass = mainClass;
    }

    @Override
    public void run() {
        mainClass.printStringB();
    }
}

public class RunClass {
    public static void main(String[] args) throws InterruptedException {
        MainClass mainClass = new MainClass();
        ThreadA threadA = new ThreadA(mainClass);
        threadA.setName("a");
        threadA.start();
        ThreadB threadB =new ThreadB(mainClass);
        threadB.setName("b");
        Thread.sleep(50);
        threadB.start();

    }
}    

输出结果是

abegin 1534849131558
bbegin 1534849131607
aend 1534849133559
bend 1534849133608

因为线程a,b持有的锁是不同的锁,所以会异步执行,若改为如下所示

public class RunClass {
    public static void main(String[] args) throws InterruptedException {
        MainClass mainClass = new MainClass();
        ThreadA threadA = new ThreadA(mainClass);
        threadA.setName("a");
        threadA.start();
        ThreadB threadB =new ThreadB(mainClass);
        threadB.setName("b");
        //Thread.sleep(50);
        threadB.start();

    }
}    

线程将会同步执行,因为同一时刻都是争抢字符串“123”对象锁。 还需要提示一下,只要对象不改变,即使对象的属性发生变化运行的结果还是同步的。 看如下代码

public class UserInfo
{
    int a;
    int b;

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }
}

public class MainClass {
    public void printStringB(UserInfo userInfo) {
        try {
            synchronized (userInfo) {
                System.out.println(Thread.currentThread().getName()+"begin " +System.currentTimeMillis());
                userInfo.setA(12);
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName()+"end " +System.currentTimeMillis());
            }
        } catch ( InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

public class ThreadA extends Thread {
    private MainClass mainClass;
    private UserInfo userInfo;

    public ThreadA(MainClass mainClass, UserInfo userInfo) {
        this.mainClass = mainClass;
        this.userInfo = userInfo;
    }

    @Override
    public void run() {
        mainClass.printStringB(userInfo);
    }
}

public class ThreadB extends Thread {
    private MainClass mainClass;
    private UserInfo userInfo;

    public ThreadB(MainClass mainClass, UserInfo userInfo) {
        this.mainClass = mainClass;
        this.userInfo = userInfo;
    }

    @Override
    public void run() {
        mainClass.printStringB(userInfo);
    }
}

public class RunClass {
    public static void main(String[] args) throws InterruptedException {
        MainClass mainClass = new MainClass();
        UserInfo userInfo = new UserInfo();
        ThreadA threadA = new ThreadA(mainClass,userInfo);
        threadA.setName("a");
        threadA.start();
        ThreadB threadB =new ThreadB(mainClass,userInfo);
        threadB.setName("b");
        //Thread.sleep(50);
        threadB.start();

    }
}

输出结果

abegin 1534849856936
aend 1534849858937
bbegin 1534849858937
bend 1534849860937

稍微运行函数

public class RunClass {
    public static void main(String[] args) throws InterruptedException {
        MainClass mainClass = new MainClass();
        UserInfo userInfo = new UserInfo();
        ThreadA threadA = new ThreadA(mainClass,userInfo);
        threadA.setName("a");
        threadA.start();
        ThreadB threadB =new ThreadB(mainClass,userInfo);
        threadB.setName("b");
        Thread.sleep(1000);
        threadB.start();

    }
}

输出结果还是

abegin 1534849955552
aend 1534849957552
bbegin 1534849957552
bend 1534849959552

足以证明即使对象属性发生变化了也不会影响线程同步。

volatile关键字

volatile关键字的作用就是是变量在多个线程之间可见,其作用就是是线程强制从公共对战中取得变量的值,而不是从线程中私有数据栈取得变量的值。

public class ThreadA extends Thread {
    private boolean isRunning = true;

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean running) {
        isRunning = running;
    }

    @Override
    public void run() {
        System.out.println("进入run了");
        while (isRunning) {}
        System.out.println("线程被停止了");
    }
}

public class RunClass {
    public static void main(String[] args) throws InterruptedException {
            try {
                ThreadA threadA = new ThreadA();
                threadA.start();
                Thread.sleep(1000);
				threadA.setRunning(false);
                System.out.println("已经被赋值了false");
            } catch (InterruptedException ex){
                ex.printStackTrace();
            }
    }
}

输出结果

进入run了
已经被赋值了false

程序一直在运行,无法停止。

程序稍微修改以后

public class ThreadA extends Thread {
    volatile private boolean isRunning = true;

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean running) {
        isRunning = running;
    }

    @Override
    public void run() {
        System.out.println("进入run了");
        while (isRunning) {}
        System.out.println("线程被停止了");
    }
}

输出结果

进入run了
线程被停止了
已经被赋值了false

通过使用关键字,强制的从内存中读取变量的值。

synchronized和volatile比较

  • 字volatile是线程同步的轻量实现,所以volatile的性能比synchronized性能要好。volatile只能修饰变量,而synchronized可以修饰方法,代码块,随着jdk的发布,synchronized性能得到了很大的提升,所以开发中使用的比率是比较大的。
  • 多线程访问volatile不会发生阻塞,synchronized会发生阻塞。
  • volatile保证数据的可见性但是不能保证原子性。synchronized可以保证原子性,也可以间接保证可见性。

volatile非原子性

先看程序

public class ThreadA extends Thread {
    volatile public static int count;

    public static void addCount() {
        for (int i = 0; i < 100; i++) {
            count++;
        }
        System.out.println("count = " + count);
    }

    @Override
    public void run() {
        addCount();
    }
}

public class RunClass {
    public static void main(String[] args) {
        ThreadA[] threadA = new ThreadA[100];
        for (int i = 0; i < 100; i++) {
            threadA[i] = new ThreadA();
        }
        for (int i = 0; i < 100; i++) {
            threadA[i].start();
        }
    }
}

输出结果

....
count = 6767
count = 6667
count = 6567
....

整个程序是异步执行的,不具备同步作用。程序稍微修改以后如下

public class ThreadA extends Thread {
    volatile public static int count;

    synchronized public static void addCount() {
        for (int i = 0; i < 100; i++) {
            count++;
        }
        System.out.println("count = " + count);
    }

    @Override
    public void run() {
        addCount();
    }
}

程序输出结果如下

...
count = 9800
count = 9900
count = 10000

这次运算准确,具备同步作用。

volatile的作用

volatile的作用主要是用于在多个线程中可以感知实例变量被更改了,并且可以获得最新值的使用,也就是多线程读取共享变量的时候可以获取最新值。

原子类也并不完全安全

原子类在具有逻辑的情况下输出结果也是具有随机性的,看如下代码

public class ThreadA extends Thread {
    public static AtomicLong count = new AtomicLong();

    public static void addCount() {
        count.addAndGet(1);
        System.out.println(Thread.currentThread().getName() + "+100以后是"+count.addAndGet(100));
    }

    @Override
    public void run() {
        addCount();
    }
}

public class RunClass {
    public static void main(String[] args) {
        ThreadA[] threadA = new ThreadA[100];
        for (int i = 0; i < 100; i++) {
            threadA[i] = new ThreadA();
        }
        for (int i = 0; i < 100; i++) {
            threadA[i].start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

输出结果

...
Thread-59+100以后是9899
Thread-62+100以后是9999
Thread-61+100以后是9798
...

再稍微修改一下代码

public class ThreadA extends Thread {
    public static AtomicLong count = new AtomicLong();

    synchronized public static void addCount() {
        count.addAndGet(1);
        System.out.println(Thread.currentThread().getName() + "+100以后是"+count.addAndGet(100));
    }

    @Override
    public void run() {
        addCount();
    }
}

输出结果

...
Thread-9+100以后是9999
Thread-22+100以后是10100

出现上述情况的原因是因为方法和方法之间的调用是异步的,所以需要加上同步才行。

synchronized有volatile同步的功能

请看如下代码

public class MainClass {
    private boolean isContinue = true;

    public void runMethod() {
        while (isContinue) {
        }
        System.out.println("程序停止下来了..");
    }

    public void stopMethod() {
        isContinue = false;
    }
}

public class ThreadA extends Thread {
   private MainClass mainClass;
    public ThreadA(MainClass mainClass) {
        this.mainClass = mainClass;
    }
    @Override
    public void run() {
        mainClass.runMethod();
    }
}

public class ThreadB extends Thread {
    private MainClass mainClass;
    public ThreadB(MainClass mainClass) {
        this.mainClass = mainClass;
    }
    @Override
    public void run() {
        mainClass.stopMethod();
    }
}

public class RunClass {
    public static void main(String[] args) throws InterruptedException {
        MainClass mainClass = new MainClass();
        ThreadA threadA = new ThreadA(mainClass);
        threadA.start();
        Thread.sleep(1000);
        ThreadB threadB = new ThreadB(mainClass);
        threadB.start();

    }
}

这个程序不会输出任何东西,代码稍作修改如下

package chapter2;

import java.util.Timer;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class MainClass {
    private boolean isContinue = true;

    public void runMethod() {
        String anyString = new String();

        while (isContinue) {
            synchronized (anyString){}
        }
        System.out.println("程序停止下来了..");
    }

    public void stopMethod() {
        isContinue = false;
    }
}

程序输出如下

程序停止下来了..

关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或者某一个代码块它含了两个特性,互斥性和可见性,同步synchronized不仅可以解决一个线程看到的对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程都看到由同一个锁保护之前的修改效果。

© 著作权归作者所有

共有 人打赏支持
简心
粉丝 28
博文 74
码字总数 144715
作品 0
济南
Spark如何使用Akka实现进程、节点通信的简明介绍

《深入理解Spark:核心思想与源码分析》一书前言的内容请看链接《深入理解SPARK:核心思想与源码分析》一书正式出版上市 《深入理解Spark:核心思想与源码分析》一书第一章的内容请看链接《第...

beliefer
2016/04/05
0
0
13篇文章,教你学会ES6知识点

ES6 深入理解ES6》学习笔记 本文用于汇总链接到各个子章节的内容,github 欢迎大家题issues和PR,如果对你有帮助,也可以给 star 支持 :) 目录 第一章 块级绑定 第二章 字符串和正则表达式 ...

你听___
05/08
0
0
数据仓库专题(16)-分布式数据仓库实践指南-目录篇

前言: 准备系统化整理一套分布式数据仓库建模实践指南,先把目录列出来吧,算是给自己设计一个目标吧。 第一部分 基础篇 第一章 数据仓库概念与定义 1.1 数据管理体系 1.2 数据仓库概念 1....

胖子哥
2015/11/12
0
0
微信小程序教学第二章(含视频):小程序中级实战教程之预备篇 - 提取util公用方法

iKcamp官网:www.ikcamp.com 访问官网更快阅读全部免费分享课程:《iKcamp出品|全网最新|微信小程序|基于最新版1.0开发者工具之初中级培训教程分享》。 包含:文章、视频、源代码 第二章:...

iKcamp
2017/10/19
0
0
《Silverlight 2完美征程》:书稿目录

内容简介 本书详细介绍了微软下一代富互联网开发技术Silverlight,分为基础篇、进阶篇、高级篇和案例篇四个部分,共22个章节,以Silverlight 2为主要版本从不同的层面进行了阐释,第一部分介...

lihuijun
2009/04/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

多线程

1. 多线程概念。并发和并行的概念。 多线程指的是一段时间内cpu同时执行多个线程。一个程序至少运行>=1个进程,进程就是运行中的程序,而一个进程至少运行>=1个线程,线程是操作系统能调度的...

鱼想吃肉
今天
0
0
HBase 表修复在线方式和离线方式

一、在线修复 1.1 使用检查命令 $ ./bin/hbase hbck 该命令可完整修复 HBase 元数据信息;存在有错误信息会进行输出; 也可以通过如下命令查看详细信息: $ ./bin/hbase hbck -details 1.2 ...

Ryan-瑞恩
今天
3
0
redis 系列二 -- 常用命令

1.基础命令 info ping quit save dbsize select flushdb flushall 2.键命令 2.1 set 直接赋值 set a a 2.2 get 取值 get a 2.3 exists 是否存在 exists a 2.4 expire 设置剩余时间 秒 expire......

imbiao
今天
2
0
php foreach

<?php// 数组的引用$a=array(1,2,3,4,5);foreach($a as $key=>&$value){$value=$value*2;}print_r($a);echo " $key -------------------$value\r\n";/** * ...

小张525
今天
3
0
12-利用思维导图梳理JavaSE-多线程

12-利用思维导图梳理JavaSE-多线程 主要内容 1.线程概念 2.线程开发 3.线程的状态 4.线程的同步和死锁 5.Java5.0并发库类 QQ/知识星球/个人WeChat/公众号二维码 本文为原创文章,如果对你有一...

飞鱼说编程
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部