线程安全问题以及解决原理:

多个线程用一个共享数据时候出现安全问题

一个经典案例:

电影院卖票,共有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(多线程二:安全问题以及解决原理)的更多相关文章

  1. Java学习笔记:多线程(二)

    与线程生命周期相关的方法: sleep 调用sleep方法会进入计时等待状态,等待时间到了,进入就绪状态. yield 调用yield方法会让别的线程执行,但是不确保真正让出.较少使用,官方注释都说 ...

  2. Java学习笔记之——多线程

    多线程编程 程序: 进程:一个程序运行就会产生一个进程 线程:进程的执行流程,一个进程至少有一个线程,称为主线程 如:QQ聊着天,同时在听音乐 一个进程可以有多个线程,多个线程共享同一个进程的资源 线 ...

  3. java学习笔记-JavaWeb篇二

    JavaWEB篇二 45 HttpSession概述46 HttpSession的生命周期 47 HttpSession常用方法示例48 HttpSessionURL重写 49 HttpSession ...

  4. 疯狂java学习笔记之面向对象(二) - 成员变量与局部变量

    Java变量按其作用域可分为:成员变量和局部变量.注意:在Java中是没有全局变量这个概念的 一.成员变量: 成员变量是在类中定义的变量,具体可分为类变量与实例变量--有无static修饰 实例变量的 ...

  5. 【Java学习笔记之十二】Java8增强的工具类:Arrays的用法整理总结

    本文将整理 java.util.Arrays 工具类比较常用的方法:  本文介绍的方法基于JDK 1.7 之上.  1.  asList方法 @SafeVarargs public static &l ...

  6. 【原】Java学习笔记032 - 多线程

    package cn.temptation; public class Sample01 { public static void main(String[] args) { /* * [进程]:正在 ...

  7. Java学习笔记:多线程(一)

    Java中线程的五种状态: 新建状态(New) 就绪状态(Runnable) 运行状态(Running) 阻塞状态(Blocked) 凋亡状态(Dead) 其中阻塞状态(Blocked)又分为三种: ...

  8. Java学习笔记(十二)——eclipse和SVN配置,导入SVN服务器项目

    [前面的话] 北京的天气外加自己的不小心终于病了,在病的过程中,感觉身体好着真好,可以学习,可以吃好吃的,可以去运动,这一病了,干什么都感觉没有力气,身体好着真好. 这个文章的背景是:领导把项目最开始 ...

  9. Java学习笔记五--String(二)String其他方法

    第一节课 // 清除单位字符串开始和结尾空白的副本 String.trim(); 字符串每次更改都会创建新的对象,而不会覆盖原来的字符串,每次拼接都会产生新的String对象,耗时耗内存. java. ...

随机推荐

  1. setTimeout应用例子-移入移出div显示和隐藏

    效果:移入div1,div2保持显示,移出div1,div2消失. 移入div2,div2保持显示,移出div2,div2消失. 一.HTML代码 <div id='div1'></ ...

  2. vue 时间过滤

    1.过滤13位时间戳(以评论时间为例) filters : { formattime2: function (value) { //value为13位的时间戳 var timestamp = Date ...

  3. Beta冲刺——第四天

    beat冲刺:第四天 各个成员今日完成的任务 成员 冯晓.马思远 彭辉.王爽 吴琼.郝延婷 今日完成任务 ·管理员功能模块的代码规范与测试 ·后台审稿系统代码规范 ·代码规范 ·系统审稿模块功能测试 ...

  4. 特大数字之和,返回结果是字符串(考虑到数字特别大,如果相加会产生e)

    自己做的,没有整理代码,还是做出来了: 做这个题时,最总要的一步思路就是,先让长度一致,然后从个位开始,每一个与每一个数字相加,如果大于10,则下一次另外两个数相加时加1 function add(a ...

  5. cmd与linux使用curl差异

    其中在用windows下的cmd 进行curl命令,出现415报错,见下,请求头使用json形式,但报错却依然提示使用的是form表单形式: 一直以为问题出在springboot的转换器做string ...

  6. javaMail实现收发邮件(三)

    JavaMail API中定义了一个java.mail.Transport类,它专门用于执行邮件发送任务,这个类的实例对象封装了某种邮件发送协议的底层实施细节,应用程序调用这个类中的方法就可以把Mes ...

  7. SpriteKit 关于categoryBitMask collisionBitMask contactTestBitMask 遇到的一些问题

    手写copy一下官方解释 首先是categoryBitMask /** 定义了这个物理刚体是属于哪个类别的掩码 .在一个场景中的每个物理刚体可以分配给达到 32 不同的类别(参数 int bitmas ...

  8. 556. Next Greater Element III下一个更大的数字

    [抄题]: Given a positive 32-bit integer n, you need to find the smallest 32-bit integer which has exac ...

  9. java_23 File类

    1.File类定义: 文件和目录路径的抽象表达形式,java中将路径或者文件封装成File对象. 2.File类的静态成员变量(pathSeparator 分隔符,字符串        separat ...

  10. salt-api配置安装 以及使用

    salt-api salt-api是我们通过restful-api调用salt-master的接口,且调用的时候必须通过认证才能调用,认证的用户为系统用户,下面就说说如何配置salt-api. 安装S ...