Java多线程——线程安全问题
一、什么情况下会产生线程安全问题?
同时满足以下两个条件时:
1,多个线程在操作共享的数据。
2,操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。
例1:四个线程卖100张票
public class TicketDemo implements Runnable {
private int tickets = 100;
public void run() {
while (true) {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "....sale:...." + tickets--);
}
}
}
public static void main(String[] args) {
TicketDemo ticketDemo = new TicketDemo();
Thread t1 = new Thread(ticketDemo);
Thread t2 = new Thread(ticketDemo);
Thread t3 = new Thread(ticketDemo);
Thread t4 = new Thread(ticketDemo);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
Thread-3....sale....100
Thread-2....sale....99
Thread-0....sale....97
Thread-1....sale....98
Thread-3....sale....96
Thread-1....sale....94
Thread-0....sale....94
Thread-2....sale....95
Thread-1....sale....93
Thread-0....sale....92
Thread-2....sale....92
Thread-3....sale....92
Thread-0....sale....91
Thread-2....sale....89
Thread-3....sale....90
Thread-1....sale....91
Thread-1....sale....88
Thread-3....sale....86
Thread-0....sale....88
Thread-2....sale....87
Thread-2....sale....84
Thread-3....sale....84
Thread-1....sale....85
Thread-0....sale....83
Thread-1....sale....82
Thread-0....sale....80
Thread-3....sale....79
Thread-2....sale....81
Thread-3....sale....78
Thread-2....sale....75
Thread-1....sale....76
Thread-0....sale....77
Thread-2....sale....74
Thread-1....sale....71
Thread-0....sale....73
Thread-3....sale....72
Thread-1....sale....70
Thread-0....sale....68
Thread-3....sale....69
Thread-2....sale....67
Thread-2....sale....66
Thread-3....sale....64
Thread-0....sale....63
Thread-1....sale....65
Thread-2....sale....62
Thread-0....sale....62
Thread-1....sale....60
Thread-3....sale....61
Thread-2....sale....59
Thread-0....sale....57
Thread-3....sale....58
Thread-1....sale....59
Thread-0....sale....56
Thread-1....sale....56
Thread-3....sale....55
Thread-2....sale....56
Thread-1....sale....54
Thread-2....sale....54
Thread-0....sale....54
Thread-3....sale....53
Thread-0....sale....52
Thread-3....sale....52
Thread-2....sale....50
Thread-1....sale....51
Thread-2....sale....49
Thread-0....sale....49
Thread-3....sale....48
Thread-1....sale....48
Thread-2....sale....46
Thread-0....sale....44
Thread-3....sale....45
Thread-1....sale....47
Thread-1....sale....43
Thread-0....sale....42
Thread-2....sale....42
Thread-3....sale....41
Thread-1....sale....40
Thread-0....sale....39
Thread-3....sale....39
Thread-2....sale....40
Thread-2....sale....38
Thread-1....sale....37
Thread-3....sale....35
Thread-0....sale....36
Thread-3....sale....34
Thread-1....sale....33
Thread-0....sale....32
Thread-2....sale....31
Thread-3....sale....30
Thread-1....sale....29
Thread-0....sale....29
Thread-2....sale....28
Thread-3....sale....27
Thread-0....sale....25
Thread-1....sale....26
Thread-2....sale....24
Thread-1....sale....23
Thread-0....sale....23
Thread-3....sale....22
Thread-2....sale....21
Thread-1....sale....20
Thread-3....sale....20
Thread-0....sale....20
Thread-2....sale....19
Thread-3....sale....16
Thread-0....sale....17
Thread-1....sale....18
Thread-2....sale....15
Thread-0....sale....13
Thread-1....sale....12
Thread-3....sale....14
Thread-2....sale....11
Thread-3....sale....10
Thread-0....sale....8
Thread-1....sale....9
Thread-2....sale....7
Thread-3....sale....6
Thread-0....sale....5
Thread-1....sale....4
Thread-2....sale....3
Thread-1....sale....2
Thread-3....sale....2
Thread-2....sale....1
Thread-0....sale....2
运行结果
观察结果,我们发现会有多个线程卖到同一张票和卖到0号票的情况,这就是线程安全问题。
解决思路:
将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不可以参与运算。
当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
在java中,用同步代码块就可以解决这个问题。
同步代码块的格式:
synchronized(对象)
{
需要被同步的代码 ;
}
这个对象一般称为同步锁。
同步的前提:同步中必须有多 个线程并使用同一个锁。
同步的好处:解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。
解决例1的线程安全问题代码:
public class TicketDemo implements Runnable {
private int tickets = 100;
Object obj = new Object();
public void run() {
while (true) {
synchronized (obj) {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "....sale...." + tickets--);
}
}
}
}
public static void main(String[] args) {
TicketDemo ticketDemo = new TicketDemo();
Thread t1 = new Thread(ticketDemo);
Thread t2 = new Thread(ticketDemo);
Thread t3 = new Thread(ticketDemo);
Thread t4 = new Thread(ticketDemo);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
二、同步锁是什么:
同步函数使用的锁是 this。
静态的同步函数使用的锁是该函数所属 字节码文件对象 ,可以用 getClass()方法获取,也可以用 当前类名.class 表示。
同步函数和同步代码块的区别:
同步函数的锁是固定的this。
同步代码块的锁是任意的对象。
建议使用同步代码块。
class Ticket implements Runnable {
private static int num = 100;
boolean flag = true;
public void run() {
if (flag)
while (true) {
synchronized (Ticket.class)//(this.getClass())同步代码块
{
if (num > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + ".....obj...." + num--);
}
}
}
else
while (true)
this.show();
}
public static synchronized void show()//同步函数
{
if (num > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + ".....function...." + num--);
}
}
}
class StaticSynFunctionLockDemo {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
t.flag = false;
t2.start();
}
}
三、死锁常见情况:
同步嵌套时,两个线程你拿了我的锁,我拿了你的锁,都不释放,造成死锁。

可以记一套死锁情况代码,面试可能用得到。
死锁情况:
class Testa implements Runnable {
private boolean flag;
Testa(boolean flag) {
this.flag = flag;
}
public void run() {
if (flag) {
while (true)
synchronized (MyLock.locka) {
System.out.println(Thread.currentThread().getName() + "..if locka....");
synchronized (MyLock.lockb) {
System.out.println(Thread.currentThread().getName() + "..if lockb....");
}
}
} else {
while (true)
synchronized (MyLock.lockb) {
System.out.println(Thread.currentThread().getName() + "..else lockb....");
synchronized (MyLock.locka) {
System.out.println(Thread.currentThread().getName() + "..else locka....");
}
}
}
}
}
class MyLock {
public static final Object locka = new Object();
public static final Object lockb = new Object();
}
class DeadLockTest {
public static void main(String[] args) {
Testa a = new Testa(true);
Testa b = new Testa(false);
Thread t1 = new Thread(a);
Thread t2 = new Thread(b);
t1.start();
t2.start();
}
}
四、单例设计模式中的线程安全问题
//饿汉式
class Single
{
private static final Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
} //懒汉式
/*
*加入同步是为了解决多线程安全问题。
*
*加入双重判断不用每次都判断是否上锁,是为了解决效率问题。
**/ class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
// -->0 -->1
s = new Single();
}
}
return s;
}
}
开发用饿汉式,没有线程安全问题。——饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
面试懒汉式,记住如何解决线程安全问题。
Java多线程——线程安全问题的更多相关文章
- Java多线程--线程安全问题的相关研究
在刚刚学线程的时候我们经常会碰到这么一个问题:模拟火车站售票窗口售票.代码如下: package cn.blogs.com.isole; /* 模拟火车站售票窗口售票,假设有50张余票 */ publ ...
- Java多线程——线程之间的协作
Java多线程——线程之间的协作 摘要:本文主要学习多线程之间是如何协作的,以及如何使用wait()方法与notify()/notifyAll()方法. 部分内容来自以下博客: https://www ...
- Java多线程——线程之间的同步
Java多线程——线程之间的同步 摘要:本文主要学习多线程之间是如何同步的,如何使用volatile关键字,如何使用synchronized修饰的同步代码块和同步方法解决线程安全问题. 部分内容来自以 ...
- java 多线程—— 线程让步
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- java 多线程—— 线程等待与唤醒
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- Java多线程--线程及相关的Java API
Java多线程--线程及相关的Java API 线程与进程 进程是线程的容器,程序是指令.数据的组织形式,进程是程序的实体. 一个进程中可以容纳若干个线程,线程是轻量级的进程,是程序执行的最小单位.我 ...
- Java基础-线程安全问题汇总
Java基础-线程安全问题汇总 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.内存泄漏和内存溢出(out of memory)的区别 1>.什么是内存溢出 答:内存溢出指 ...
- Java多线程-线程的同步(同步方法)
线程的同步是保证多线程安全访问竞争资源的一种手段.线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源.什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些 ...
- Java多线程——线程的优先级和生命周期
Java多线程——线程的优先级和生命周期 摘要:本文主要介绍了线程的优先级以及线程有哪些生命周期. 部分内容来自以下博客: https://www.cnblogs.com/sunddenly/p/41 ...
随机推荐
- hdu 1195 Open the Lock (BFS)
Open the Lock Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tot ...
- 转:用VMProtect和ASProtect 的SDK加密应用程序
最近想用VMProtect和ASProtect 的SDK加密一个程序,结果搞了半天没搞成,网上没看到在VC中如何使用VMProtect的SDK加密,于是琢磨了一下,总算成功了,最后有一点点心得,与大家 ...
- 除了IE浏览器,其他浏览器都联不上网怎么办~转载百度经验
百度师傅最快的到家服务,最优质的电脑清灰 百度经验:jingyan.baidu.com 有个网友遇到一个非常奇怪的上网问题,刚才始,发现QQ不能登录,后来接着发现火狐浏览器也打不开网页,刚开始,以为只 ...
- PHP开发笔记(一)
Location of the Android sdk has not been setup in the preference. 分析与解决: 第一次安装好adt后, 选择android sdk的路 ...
- v4l2 spec 中文 Ch01【转】
转自:http://blog.csdn.net/wuhzossibility/article/details/6638245 目录(?)[-] Chapter 1 通用APICommon API El ...
- 深入解析Linux内核I/O剖析(open,write实现)
Linux内核将一切视为文件,那么Linux的文件是什么呢?其既可以是事实上的真正的物理文件,也可以是设备.管道,甚至还可以是一块内存.狭义的文件是指文件系统中的物理文件,而广义的文件则可以是Linu ...
- POCO库中文编程参考指南(4)Poco::Net::IPAddress
POCO库中文编程参考指南(4)Poco::Net::IPAddress 作者:柳大·Poechant 博客:Blog.CSDN.net/Poechant 邮箱:zhongchao.ustc#gmai ...
- c#操作SQL的例子
>> 数据表复制 当表目标表存在时: insert into 目的数据库..表 select * from 源数据库..表 当目标表不存在时: select * into 目的数据库..表 ...
- 左侧菜单栏右侧内容(改进,有js效果)
(如有错敬请指点,以下是我工作中遇到并且解决的问题)上一篇文章是简洁版 这是上一篇文章的改进. 上一篇文章的左侧菜单是没有子目录的. 这是效果图: 左侧菜单代码: <div class=&quo ...
- springBoot【01】
/* 使用spring官网的 http://start.spring.io/ 来建立项目包 生成入口文件,入口文件中对类注释@SpringBootApplication,这个注释是唯一的,标明这个类是 ...