Java学习笔记45(多线程二:安全问题以及解决原理)
线程安全问题以及解决原理:
多个线程用一个共享数据时候出现安全问题
一个经典案例:
电影院卖票,共有100座位,最多卖100张票,买票方式有多种,网上购买、自主售票机、排队购买
三种方式操作同一个共享数据,这时候会出现安全问题:
示例:
package demo1;
public class Tickets implements Runnable {
private int ticket = 100;
public void run(){
while(true){
if (ticket>0) {
System.out.println(Thread.currentThread().getName()+"出售第"+ticket--+"张票");
}
}
}
}
package demo1;
public class ThreadDemo {
public static void main(String[] args) {
Tickets t = new Tickets();
Thread t0 = new Thread(t);
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t0.start();
t1.start();
t2.start();
}
}
一般不会出现问题,但是要想到这种问题
但是,假设只剩下最后最后一张票,一个线程抢到CPU资源执行,在判断结束时候,CPU资源被其他线程抢到,其他线程判断然后执行,
这时候轮到开始时候的线程,由于已经判断完,继续执行,这时候票数就会变成负数,这里就出现了问题
解决方法:
同步代码块
原理:一个线程进入数据操作的时候,阻止其他线程执行
package demo1;
public class Tickets implements Runnable {
private int ticket = 100;
private Object obj1 = new Object();
public void run() {
while (true) {
synchronized (obj1) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");
}
}
}
}
}
不过,虽然安全了,但是运行速度下降
但是,我们为了安全性可以不顾及速度,无论如何都要保证安全性
这里传入的对象参数简称作:同步锁,专业名称:对象监视器
原理:
没有锁的线程不能执行,只能等待
线程遇到同步代码块后判断是否有同步锁,如果有,拿走锁,进入同步中执行,执行完毕后将锁对象还回去
另一个线程遇到代码块后没有锁,无法进入,原来的线程把锁还回去之后新线程再获取锁,循环下去
这里明显可以看出,这么多的过程,速度自然就慢下来了
采用同步方法解决问题:
优点:代码量更低
package demo1;
public class Tickets implements Runnable {
private int ticket = 100;
public void run() {
while (true) {
payTicket();
}
}
public synchronized void payTicket() {
//同步方法的对象锁是本类对象引用:即为this
//静态方法的锁是本类类名.class
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");
}
}
}
缺点:如果出现了异常,方法的锁对象没有释放,不出同步,锁不会释放
这里就需要用到一个Lock接口:
提供了更广泛的锁定操作
改进之前的售票案例:
package demo1; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class Tickets implements Runnable {
private int ticket = 100;
private Lock lock = new ReentrantLock(); public void run() {
while (true) {
lock.lock();
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");
}
lock.unlock();
}
}
}
死锁:
同步锁引发的弊端:
当线程任务中出现了多个同步时,如果同步中嵌套了其他的同步,这时候就会引发一种现象,程序出现无限等待,这种现象称之为死锁
通俗解释:两个人吃一碗面,却只有一双筷子,两个人一人抢到一支筷子,规定不能用手抓,这时候就无法吃面
代码实现:
package demo1;
public class LockA {
private LockA(){}
public final static LockA locka =new LockA();
}
package demo1;
public class LockB {
private LockB(){}
public final static LockB lockb =new LockB();
}
package demo1;
public class DeadLock implements Runnable {
private int i = 0;
public void run() {
while (true) {
if (i % 2 == 0) {
synchronized (LockA.locka) {
System.out.println("if...locka");
synchronized (LockB.lockb) {
System.out.println("if...lockb");
}
}
} else {
synchronized (LockB.lockb) {
System.out.println("else...lockb");
synchronized (LockA.locka) {
System.out.println("else...locka");
}
}
}
i++;
}
}
}
package demo1;
public class DeadLockDemo {
public static void main(String[] args) {
DeadLock dead = new DeadLock();
Thread t0 = new Thread(dead);
Thread t1 = new Thread(dead);
t0.start();
t1.start();
}
}
运行后发现,会卡在某一处不动,但是并没有停止
Java学习笔记45(多线程二:安全问题以及解决原理)的更多相关文章
- Java学习笔记:多线程(二)
与线程生命周期相关的方法: sleep 调用sleep方法会进入计时等待状态,等待时间到了,进入就绪状态. yield 调用yield方法会让别的线程执行,但是不确保真正让出.较少使用,官方注释都说 ...
- Java学习笔记之——多线程
多线程编程 程序: 进程:一个程序运行就会产生一个进程 线程:进程的执行流程,一个进程至少有一个线程,称为主线程 如:QQ聊着天,同时在听音乐 一个进程可以有多个线程,多个线程共享同一个进程的资源 线 ...
- java学习笔记-JavaWeb篇二
JavaWEB篇二 45 HttpSession概述46 HttpSession的生命周期 47 HttpSession常用方法示例48 HttpSessionURL重写 49 HttpSession ...
- 疯狂java学习笔记之面向对象(二) - 成员变量与局部变量
Java变量按其作用域可分为:成员变量和局部变量.注意:在Java中是没有全局变量这个概念的 一.成员变量: 成员变量是在类中定义的变量,具体可分为类变量与实例变量--有无static修饰 实例变量的 ...
- 【Java学习笔记之十二】Java8增强的工具类:Arrays的用法整理总结
本文将整理 java.util.Arrays 工具类比较常用的方法: 本文介绍的方法基于JDK 1.7 之上. 1. asList方法 @SafeVarargs public static &l ...
- 【原】Java学习笔记032 - 多线程
package cn.temptation; public class Sample01 { public static void main(String[] args) { /* * [进程]:正在 ...
- Java学习笔记:多线程(一)
Java中线程的五种状态: 新建状态(New) 就绪状态(Runnable) 运行状态(Running) 阻塞状态(Blocked) 凋亡状态(Dead) 其中阻塞状态(Blocked)又分为三种: ...
- Java学习笔记(十二)——eclipse和SVN配置,导入SVN服务器项目
[前面的话] 北京的天气外加自己的不小心终于病了,在病的过程中,感觉身体好着真好,可以学习,可以吃好吃的,可以去运动,这一病了,干什么都感觉没有力气,身体好着真好. 这个文章的背景是:领导把项目最开始 ...
- Java学习笔记五--String(二)String其他方法
第一节课 // 清除单位字符串开始和结尾空白的副本 String.trim(); 字符串每次更改都会创建新的对象,而不会覆盖原来的字符串,每次拼接都会产生新的String对象,耗时耗内存. java. ...
随机推荐
- 深入剖析GPU Early Z优化
最近在公司群里同事发了一个UE4关于Mask材质的优化,比如在场景中有大面积的草和树的时候,可以在很大程度上提高效率.这其中的原理就是利用了GPU的特性Early Z,但是它的做法跟我最开始的理解有些 ...
- cacti的介绍、安装、配置、及维护
一.cacti的介绍 Cacti 在英文中的意思是仙人掌的意思,Cacti是一套基于PHP,MySQL,SNMP及RRDTool开发的网络流量监测图形分析工具.它通过snmpget来获取数据,使用 R ...
- python命名规则
1 包.模块的命名规则:全部以小写字母形式来命名.比如:import random 2 类.对象的命名规则:类是每个单词的首字母要大写,其他字母小写比如:class MyFamily: ,类的私有属性 ...
- JavaSE基础知识(5)—面向对象(5.3访问修饰符)
一.说明 访问修饰符可以用于修饰类或类的成员(属性.方法.构造器.内部类) 二.特点 名称 本类 本包 其他包的子类 其他包的非子类 private 私有的 √ × × × 缺省 默认 √ √ × ...
- c++矩阵运算库Eigen简介
C++矩阵运算库Eigen介绍 C++中的矩阵运算库常用的有Armadillo,Eigen,OpenCV,ViennaCL,PETSc等.我自己在网上搜了一下不同运算库的特点,最后选择了Eigen.主 ...
- 15. pk-mext
在平时的生产环境中,我们经常会碰到监控MySQL的各个状态值的一个变化趋势,然后就会自己写个脚本,将status快照保存到文本中.当我们去分析的时候,需要自己去比较差值,是一件比较麻烦的时候,虽然可以 ...
- python02 运算符,基本数据类型,整型,字符串
1.python开发IDE pycharm,python编写工具,, #专业版 #不需要汉化 注册码问题解决 https://www.cnblogs.com/evlon/p/4934705.html整 ...
- Chapter3_操作符_方法调用中的别名问题
接下来展示方法调用中的别名问题,方法调用中的别名问题指的是,将一个对对象的引用传递给某一个方法时,方法操作的是这一个特定的引用而不是这个引用的拷贝. class Person{ float heigh ...
- urlrewrite的rule怎么写
一.正则表达式教程 常用正则如下: . 换行符以外的所有字符 \w 匹配字母或数字或下划线或汉字 \s 匹配任意的空白符 \d ...
- javascript 取整,取余数 math方法
1.丢弃小数部分,保留整数部分 parseInt() 函数可解析一个字符串,并返回一个整数. parseInt(string, radix) 参数 描述 string 必需.要被解析的字符串. rad ...