Java Thread系列(三)线程安全

一、什么是线程安全

线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。

线程安全来说,需要满足以下两个特性:

  • 原子性

  • 可见性

public class MyThread extends Thread {

    private int count = 5;

    //synchronized加锁 同步锁
public /*synchronized*/ void run () {
System.out.printf("线程%s执行,count:%s\n", this.currentThread().getName(),--count);
} public static void main(String[] args) {
/**
* 当多个线程访问 MyThread 的 run 方法时,以排队的方式进行处理(这里的排序是按照 CPU 分配的先后顺序而定的)
* 一个线程要执行 synchronized 修饰的方法时
* 1. 尝试获得锁,如果拿到锁则执行该方法
* 2. 这个线程就会不断尝试获得这把锁,直到拿到为止,而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题)
*/
MyThread thread = new MyThread();
new Thread(thread, "t1").start();
new Thread(thread, "t2").start();
new Thread(thread, "t3").start();
new Thread(thread, "t4").start();
new Thread(thread, "t5").start();
new Thread(thread, "t6").start();
}
}

执行结果:

//MyThread 的 run() 方法不加锁 synchronized
线程t1执行,count:4
线程t4执行,count:1
线程t5执行,count:1
线程t3执行,count:2
线程t2执行,count:3 //run() 加锁 synchronized,期待的结果
线程t1执行,count:4
线程t2执行,count:3
线程t5执行,count:2
线程t4执行,count:1
线程t3执行,count:0

由此可见:

  1. 多个线程要执行 synchronized 修饰的方法时,必须获取对象锁,如果得不到这把锁,就处于等待状态,直到获取这把锁才执行这个方法。

二、什么是锁:对象锁和类锁

多个线程多个锁,多个线程,每个线程都可以拿到自己指定的锁,分别获得锁后执行 synchronized 修辞的方法。

  • 同步(synchronized)的概念就是共享,我们要牢牢记住“共享”这两个字,如果不是共享的资源,就没有必要进行同步。

  • 异步(asynchronized)的概念就是独立,相互之间不受任何制约。eg: Ajax

public class MutiThread {

    private /*static*/ int num = 0;

    /**
* synchronized:对象锁,两个对象,线程获得的就是两个不同的锁,互不影响
* static synchronized:表示类级别的锁,即便多个对象也是相同的锁
*/
public /*static*/ synchronized void printNum (String tag) {
try {
if("a".equalsIgnoreCase(tag)) {
System.out.printf("tag %s 设置成功\n", tag);
num = 100;
Thread.sleep(1000);
} else {
System.out.printf("tag %s 设置成功\n", tag);
num = 200;
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.printf("tag %s , num=%s\n", tag, num);
} public static void main(String[] args) { final MutiThread m1 = new MutiThread();
final MutiThread m2 = new MutiThread();
new Thread(new Runnable() {
public void run() {
m1.printNum("a");
}
}).start();
new Thread(new Runnable() {
public void run() {
m1.printNum("b");
}
}).start();
}
}

执行结果:

//run()方法用synchronized修辞
tag b 设置成功
tag b , num=200
tag a 设置成功
tag a , num=100 //run()方法用static synchronized修辞
tag a 设置成功
tag a , num=100
tag b 设置成功
tag b , num=200

由此可见:

  1. static synchronized 修辞的方法,属于类级别的锁,多个对象共享同一把锁。

  2. synchronized 修辞的方法,属于对象锁,一个对象一把锁。

三、脏读

对于对象的同步和异步的方法,一定要考虑问题的整体性,不然就会出现数据不一致的错误,很经典的错误就是脏读(dirtyread)

public class DirtyRead {

    private String username;
private String password; public /*synchronized*/ void setValue(String username, String password) {
this.username = username; try {
Thread.sleep(2000);
} catch (InterruptedException e) {
//e.printStackTrace();
}
this.password = password;
System.out.printf("username=%s; password=%s\n", username, password);
} public /*synchronized*/ void getValue() {
System.out.printf("username=%s; password=%s\n", username, password);
} public static void main(String[] args) throws InterruptedException {
final DirtyRead dirtyRead = new DirtyRead();
new Thread(new Runnable() {
public void run() {
dirtyRead.setValue("admin", "admin");
}
}).start();
Thread.sleep(1000);
dirtyRead.getValue();
//username=admin; password=null 不加锁,产生脏读
//username=admin; password=admin 加锁
}
}

执行结果:

  1. setValue() 执行要 2s,而主程序 1s 时调用 getValue() ,这时username 已经赋值,而 password 仍未赋值,产生了脏读,username=admin; password=null

  2. setValue() 和 getValue() 都对 username 和 password 操作,所以要避免产生脏读,需要对这两个方法都加锁 synchronized。


每天用心记录一点点。内容也许不重要,但习惯很重要!

Java Thread系列(三)线程安全的更多相关文章

  1. Java Thread系列(四)线程通信

    Java Thread系列(四)线程通信 一.传统通信 public static void main(String[] args) { //volatile实现两个线程间数据可见性 private ...

  2. Java Thread系列(二)线程状态

    Java Thread系列(二)线程状态 一.线程的五种状态 新建状态(New):新创建了一个线程对象,尚未启动. 就绪状态(Runnable):也叫可运行状态.线程对象创建后,其他线程调用了该对象的 ...

  3. Java Thread系列(一)线程创建

    Java Thread系列(一)线程创建 Java 中创建线程主要有三种方式:继承 Thread.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实 ...

  4. Java多线程系列--“JUC线程池”04之 线程池原理(三)

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...

  5. Java多线程系列--“JUC线程池”06之 Callable和Future

    概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...

  6. Java多线程系列--“JUC线程池”03之 线程池原理(二)

    概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...

  7. Java多线程系列--“JUC线程池”05之 线程池原理(四)

    概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...

  8. Java Thread系列(十)Future 模式

    Java Thread系列(十)Future 模式 Future 模式适合在处理很耗时的业务逻辑时进行使用,可以有效的减少系统的响应时间,提高系统的吞吐量. 一.Future 模式核心思想 如下的请求 ...

  9. java: Thread 和 runnable线程类

    java: Thread 和 runnable线程类 Java有2种实现线程的方法:Thread类,Runnable接口.(其实Thread本身就是Runnable的子类) Thread类,默认有ru ...

随机推荐

  1. FastAdmin 升级后出现 is already in use

    FastAdmin 升级后出现 is already in use 升级 FastAdmin 改进很多,但全新安装出现以下错误 Cannot use app\common\library\Menu a ...

  2. vCenter 6.5安装

    http://guanjianfeng.com/archives/1160269 最近,VMware发布了vSphere 6.5版本,之前的最新版本为6.0.新版本已经开始试行使用HTML5来管理vS ...

  3. [C++ Primer] 第7章: 类

    定义抽象数据类型 定义在类内部的函数是隐式的inline函数. const成员函数 又叫做常量成员函数, 成员函数参数列表之后紧跟const关键字, const修饰的是类this指针. 默认情况下th ...

  4. PHP 设计模式 原型模式(Prototype)之深/浅拷贝

      看PHP 设计模式 原型模式(Prototype)时,衍生出一个扩展问题之 原型拷贝的浅拷贝和深拷贝问题(不管写Java还是写PHP还是写JS时都多多少少遇到过对象拷贝问题)   比如写前端页面时 ...

  5. Appcan、apicloud、HBuilder 不同之处解析

    来源:http://www.mamicode.com/info-detail-1129829.html 现在Hybrid app是一中非常火热的开发模式,在国内对应的开发工具也乱象丛生,有WeX5.c ...

  6. Bootstrap学习之路(1)---开篇-登陆页

    Bootstrap是现在很流行的一套前端框架,尤其是它的自适应,真的很不错,而且对移动设备也很友好,可以达到快速开发的效果,最近想把自己的网站弄个手机版,很果断的就选用了bootstrap,跟大家分享 ...

  7. win7 php5.6 redis扩展

    步骤: 1.下载redis扩展 redis扩展下载地址:http://windows.php.net/downloads/pecl/snaps/redis/ 查看phpinfo下载匹配的版本(一定要选 ...

  8. Java利用ScriptEngineManager对计算公式的支持

    1.ScriptEngineManager是JDK6提出的相关方法,这方式的主要目的就是用来对脚本语言的处理.这里只是简单介绍一下对我们常用的数学公式的应用. 2.ScriptEngineManage ...

  9. DSL与编译原理

    DSL:领域语言 类似于SQL的一种语言,比如自创一种语言,如何解析 可以自己实现类似于一种语言: 比如hibernate里面的sql解析就使用ANTLR 比如:http://hellojinjie. ...

  10. PDF预览之PDFObject.js总结

    get from:PDF预览之PDFObject.js总结   PDFObject.js - 将PDF嵌入到一个div内,而不是占据整个页面(要求浏览器支持显示PDF,不支持,可配置PDF.js来实现 ...