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

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

一个经典案例:

电影院卖票,共有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. ArrayList实现动态数组原理

    addAll方法和申请数组大小函数 public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); ...

  2. Python导出MySQL数据库中表的建表语句到文件

    为了做数据对象的版本控制,需要将MySQL数据库中的表结构导出成文件进行版本化管理,试写了一下,可以完整导出数据库中的表结构信息 # -*- coding: utf-8 -*- import os i ...

  3. ceph常用运维技巧总结1

    格式 json 数据增强可读性 --format json-pretty -f json-pretty ceph quorum_status -f json-pretty ceph mon_statu ...

  4. Python代码的人机大战(循环嵌套)

    第一次动手写随笔,记录一下今早的1.5小时努力成果 题目是这样的 : 人和机器进行猜拳游戏写成一个类,首先选择角色:1 曹操 2张飞 3 刘备,然后选择的角色进行猜拳:1剪刀 2石头 3布 玩家输入一 ...

  5. C++ 11 创建和使用 unique_ptr

    unique_ptr 不共享它的指针.它无法复制到其他 unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL) 算法.只能移动unique_ptr.这意味着,内存资 ...

  6. python--第十天总结(IO多路复用)

    服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型. (2)同步非阻塞IO(Non-blocking IO):默认创建的s ...

  7. Python+Selenium学习--下载文件

    场景 webdriver 允许我们设置默认的文件下载路径.也就是说文件会自动下载并且存在设置的那个目录中,下面以firefox及chrome为例 代码 Firefox下载 为了让Firefox浏览器能 ...

  8. oracle优化技巧及实例(总结)

    1.关于exists和in in是循环的方式,在内存中处理, exists是执行数据库查询, select tpd.personaccountid,sum(nvl(tpd.CREDIT_SUM, 0) ...

  9. 创建第一次C语言程序

    在这里我以VS2015为例,做演示.为什么要去演示怎样创建项目尼,因为我写第一个程序时,不知道该怎样用VS创建我的第一个应用程序. 第一步:打开VS环境如下 第二步:在开始出点击“新建项目”或在右上角 ...

  10. 【c++】内存检查工具Valgrind介绍,安装及使用以及内存泄漏的常见原因

    转自:https://www.cnblogs.com/LyndonYoung/articles/5320277.html Valgrind是运行在Linux上一套基于仿真技术的程序调试和分析工具,它包 ...