Java Thread系列(三)线程安全
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
由此可见:
- 多个线程要执行 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
由此可见:
static synchronized 修辞的方法,属于类级别的锁,多个对象共享同一把锁。
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 加锁
}
}
执行结果:
setValue() 执行要 2s,而主程序 1s 时调用 getValue() ,这时username 已经赋值,而 password 仍未赋值,产生了脏读,username=admin; password=null
setValue() 和 getValue() 都对 username 和 password 操作,所以要避免产生脏读,需要对这两个方法都加锁 synchronized。
每天用心记录一点点。内容也许不重要,但习惯很重要!
Java Thread系列(三)线程安全的更多相关文章
- Java Thread系列(四)线程通信
Java Thread系列(四)线程通信 一.传统通信 public static void main(String[] args) { //volatile实现两个线程间数据可见性 private ...
- Java Thread系列(二)线程状态
Java Thread系列(二)线程状态 一.线程的五种状态 新建状态(New):新创建了一个线程对象,尚未启动. 就绪状态(Runnable):也叫可运行状态.线程对象创建后,其他线程调用了该对象的 ...
- Java Thread系列(一)线程创建
Java Thread系列(一)线程创建 Java 中创建线程主要有三种方式:继承 Thread.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实 ...
- Java多线程系列--“JUC线程池”04之 线程池原理(三)
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- Java多线程系列--“JUC线程池”03之 线程池原理(二)
概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...
- Java多线程系列--“JUC线程池”05之 线程池原理(四)
概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...
- Java Thread系列(十)Future 模式
Java Thread系列(十)Future 模式 Future 模式适合在处理很耗时的业务逻辑时进行使用,可以有效的减少系统的响应时间,提高系统的吞吐量. 一.Future 模式核心思想 如下的请求 ...
- java: Thread 和 runnable线程类
java: Thread 和 runnable线程类 Java有2种实现线程的方法:Thread类,Runnable接口.(其实Thread本身就是Runnable的子类) Thread类,默认有ru ...
随机推荐
- WCF WS-Security and WSE Nonce Authentication【转】
原文:http://weblog.west-wind.com/posts/2012/Nov/24/WCF-WSSecurity-and-WSE-Nonce-Authentication?utm_sou ...
- django-redis 中文文档
Andrey Antukh, niwi@niwi.be 4.7.0 翻译: RaPoSpectre 1. 介绍 django-redis 基于 BSD 许可, 是一个使 Django 支持 Redis ...
- udev笔记
1.udevd的主配置文件是/etc/udev/udev.conf 2.使用udev来监听U的hot-plug事件 #include <stdio.h> #include <stdl ...
- hasura graphql schema 导出
使用的是apollo 的插件 安装apollo npm install -g apollo 基本使用 因为我使用了模式拼接,所以地址有变动,一般是 http://host:port/v1alpha1/ ...
- chaos-engineering 的一些开源工具
Chaos Monkey - A resiliency tool that helps applications tolerate random instance failures. The Simi ...
- Scriptcase v8推出内部測试版及价格调整
PHP代码生成器Scriptcase如今已经内部推出了v8.0版本号,8.0相比于曾经的版本号做了大幅度调整.我们将在完好測试的基础上发表8.0的相关文章. 另,即日起,Scriptcase的20%优 ...
- 公共的Json操作类
using System; using System.Data; using System.Text; using System.Collections.Generic; using System.R ...
- postgresql PL/pgSQL—存储过程结构和变量声明
ref: https://www.postgresql.org/docs/9.6/static/plpgsql-structure.html 一. 函数结构 CREATE FUNCTION somef ...
- [C++ Primer] 第7章: 类
定义抽象数据类型 定义在类内部的函数是隐式的inline函数. const成员函数 又叫做常量成员函数, 成员函数参数列表之后紧跟const关键字, const修饰的是类this指针. 默认情况下th ...
- 我的第一个php扩展
一.进入php源码包,找到ext文件夹 cd /owndata/software/php-5.4.13/ext 文件夹下放的都是php的相关扩展模块 二.生成自己的扩展文件夹和相关文件 php支持开发 ...