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. ...
随机推荐
- 悲观锁,乐观锁,排他锁,行锁----MYSQL
在说具体的锁结构时,先思考一个问题,那就是为什么要上锁?然后我要如何选择锁?锁具体如何实现? 在文章得末尾我给出了我的个人答案. 一.什么是悲观锁? 1.悲观锁就是在操作数据时,认为此操作会出现数据冲 ...
- JavaScript: Constructor and Object Oriented Programming
Constructor : Grammar: object.constructor Example: Javascript code: 1 function obj1() { this.number ...
- Android 滑块验证
先上图看看实现效果 1.在 app 的 build.gradle 添加依赖 implementation 'com.luozm.captcha:captcha:1.1.2' 2.将 Captcha 添 ...
- jquery全国省市区三级联动插件distpicker
使用步骤: 1.引入js <script src="distpicker/jquery.min.js" type="text/javascript" ch ...
- input text 只能输入数字
添加 onkeyup="value=value.replace(/[^\d]/g,'')"
- 113. Path Sum II 输出每个具体路径
[抄题]: Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the gi ...
- Java中两个线程是否可以同时访问同一个对象的两个不同的synchronized方法?
不可以!!! 多个线程访问同一个类的synchronized方法时, 都是串行执行的 ! 就算有多个cpu也不例外 ! synchronized方法使用了类java的内置锁, 即锁住的是方法所属对象本 ...
- Linux驱动之poll机制的理解与简单使用
之前在Linux驱动之按键驱动编写(中断方式)中编写的驱动程序,如果没有按键按下.read函数是永远没有返回值的,现在想要做到即使没有按键按下,在一定时间之后也会有返回值.要做到这种功能,可以使用po ...
- MySQL(数据类型和完整约束)
MySQL数据类型 MySQL支持多种数据类型,主要有数值类型.日期/时间类型和字符串类型. 1.数值数据类型 包括整数类型TINYINT.SMALLINT.MEDIUMINT.INT.BIGINT. ...
- css初始
css概念及作用 css即层叠样式表的英文缩写 作用:1 渲染页面 2 页面布局 css语法 CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明. 格式: selector{ prope ...