Java多线程--线程安全问题的相关研究
在刚刚学线程的时候我们经常会碰到这么一个问题:模拟火车站售票窗口售票。代码如下:
package cn.blogs.com.isole;
/*
模拟火车站售票窗口售票,假设有50张余票
*/
public class _synchronizeds {
public static void main(String[] args) {
//创建3个线程对象,分别代表售票的3个窗口
Ticket t1 = new Ticket("窗口1");
Ticket t2 = new Ticket("窗口2");
Ticket t3 = new Ticket("窗口3");
//开启线程,开始卖票
t1.start();
t2.start();
t3.start();
}
}
//售票
class Ticket extends Thread{
//定义剩余票,静态的
static int num=50;
//构造方法为线程重新命名
public Ticket(String name){
super(name);
}
//重写run()方法,将自定义线程代码写入
@Override
public void run() {
while(true){
if(num>0){
num--;
System.out.println(Thread.currentThread().getName()+"卖出了"+num+"号票");
}else{
System.out.println("票售罄了");
break;
}
}
}
}
多次启动线程测试发现问题如下图:

这个就是线程的安全问题了!
在什么情况下才可能出现线程安全问题:
1.必须存在多个线程对象,而且线程之间共享着一个资源
2.必须存在多个语句操作了共享资源,如下方if内的2句代码
线程安全的解决方案:
思路:在共享资源里设定在一个时间片只允许一个线程完全操作完毕之后才允许其他线程操作
sun提供了线程同步机制让我们解决这类线程安全问题的:
方式1.同步代码块:
同步代码块的格式:
synchronized(锁对象){
需要被同步的代码...
}
要注意的事项:
1.任意的一个对象都可以作为锁对象(凡是对象 内部都维护了一个状态的,例如state = 1 开 0关
java的同步机制就是使用了对象中的状态作为了锁的标识)
2.在同步代码块中调用了sleep方法并不会释放锁对象的,而是暂停执行一段时间再继续执行
3.只有真正存在线程安全问题的时候才使用同步代码块,否则会降低效率 因为每次都得判断锁的状态是开还是关
4.多线程操作的锁对象必须是唯一共享的,否则无效(static)因为不同的话只能锁自己的锁
方式2.同步函数:就是使用synchronized修饰函数
同步函数要注意的事项:
1.如果是一个非静态的同步函数的锁 对象是this对象,如果是静态的同步函数的锁 对象是当前函数所属的类的字节码文件(class对象)
2.同步函数的锁对象是固定的,不能被指定
针对以上2种方案推荐使用:同步代码块
原因.同步代码块的锁对象可以由我们随意指定,方便控制
以下代码即使用同步代码块解决上述的问题:
package cn.blogs.com.isole;
/*
模拟火车站售票窗口售票,假设有50张余票
*/
public class _synchronizeds {
public static void main(String[] args) {
//创建3个线程对象,分别代表售票的3个窗口
Ticket t1 = new Ticket("窗口1");
Ticket t2 = new Ticket("窗口2");
Ticket t3 = new Ticket("窗口3");
//开启线程,开始卖票
t1.start();
t2.start();
t3.start();
}
}
//售票
class Ticket extends Thread{
//定义剩余票,静态的
static int num=50;
//构造方法为线程重新命名
public Ticket(String name){
super(name);
}
//重写run()方法,将自定义线程代码写入
@Override
public void run() {
while(true){
synchronized("锁的对象可以是任意的,但是要共享"){//锁开始的位置,当线程执行到这里的时候,锁的状态值变成0代表不可进入
if(num>0){
num--;
System.out.println(Thread.currentThread().getName()+"卖出了"+num+"号票");
}else{
System.out.println("票售罄了");
break;
}
}//锁的结束位置,当线程执行到这里,锁的状态变成1代表可进入
}
}
}
这个时候执行就不会出现线程安全的相关问题了
线程的通讯机制(模拟生产者与消费者之间的产品关系)代码如下:
class Product{
String name;//名字
double price;//价格
boolean flag = false;//产品生产的标识
}
//生产者
class Producer extends Thread{
Product p ; //产品
public Producer(Product p){
this.p = p;
}
public void run(){
int i = 0;
while(true){
synchronized(p) {
if(p.flag == false){//如果没有生产
if(i%2==0){
p.name = "苹果";
p.price = 6.5;
}else{
p.name = "香蕉";
p.price = 1.1;
}
System.out.println("生产者生产出了:"+p.name+"价格是:"+p.price);
i++;
p.flag=true;
p.notify();//唤醒消费者去消费
}else{
//生产者生产完毕,等待消费者消费
try {
p.wait();//由锁对象调用
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}//锁结束
}//循环结束
}
}
//消费者
class Customer extends Thread{
Product p;
public Customer(Product p){
this.p = p;
}
public void run(){
while(true){
synchronized(p){
if(p.flag == true){//如果有生产完毕的产品
System.out.println("消费者消费了:"+p.name+"价格:"+p.price);
p.flag = false;
p.notify();//唤醒生产者生产
}else{
//产品还没有生产,应该等待生产者先生产
try {
p.wait();//消费者等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}//锁结束
}
}
}
public class _8Demo_Thread4 {
public static void main(String[] args) {
Product p = new Product();//产品
Producer p1 = new Producer(p);//生产者
Customer p2 = new Customer(p);//消费者
p1.start();
p2.start();
}
}
线程的通讯:一个线程完成了自己的任务时,要通知另外一个线程去完成另外一个任务
生产者-消费者 生产者的产品给消费者使用 产品是共享的
问题1:出现了线程安全问题(价格错乱了)
加synchronized锁住 且锁的对象使用p
问题2:生产者生产完产品之后才能被消费者使用
线程通信的2个方法
wait(); //等待 如果执行则该线程进入等待的状态 必须要其他线程调用notify()才能唤醒
notify(); 唤醒等待的线程
wait()与notify()方法要注意的事项:
1.这2个方法是属于Object对象的 原因:锁的对象是我们自己定义的,而不是Thread定义的,所以调用这两个方法的对象可能是任意的 而任意的类都是Object类的子类
2.wait方法与notify方法必须在同步代码块或者是同步函数中才能使用
3.wait方法与notify必须要由锁对象调用
wait:一个线程如果执行了wait方法,那么该线程就会进去一个以"锁对象"为标识符的线程池中等待 一旦执行wait会释放锁
notify();如果一个线程执行notify方法,那么就会唤醒以锁对象为标识符的线程池中等待线程中其中一个
notifyAll(); //唤醒线程池中所有等待的线程
附:死锁问题
java的同步机制解决了线程安全问题,但是也同时引发死锁的现象
死锁现象存在的根本原因:
1.存在2个或2个以上的线程
2.存在的共享资源个数大于等于2个
死锁现象的解决方案:没有方案,只能尽量的避免发生而已...
附:守护线程
1.守护线程:就是main同生共死,随着main的结束而结束,而普通线程是在任务代码执行结束才停止。
2.用户线程:Java虚拟机在它所有非守护线程已经离开后自动离开。守护线程则是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去。
例如:我们所熟悉的Java垃圾回收线程就是一个典型的守护线程,当我们的程序中不再有任何运行中的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是Java虚拟机上仅剩的线程时,Java虚拟机会自动离开。
Java多线程--线程安全问题的相关研究的更多相关文章
- Java多线程——线程安全问题
一.什么情况下会产生线程安全问题? 同时满足以下两个条件时: 1,多个线程在操作共享的数据.2,操作共享数据的线程代码有多条. 当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导 ...
- Java多线程--线程及相关的Java API
Java多线程--线程及相关的Java API 线程与进程 进程是线程的容器,程序是指令.数据的组织形式,进程是程序的实体. 一个进程中可以容纳若干个线程,线程是轻量级的进程,是程序执行的最小单位.我 ...
- 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基础-线程安全问题汇总 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.内存泄漏和内存溢出(out of memory)的区别 1>.什么是内存溢出 答:内存溢出指 ...
- Java多线程-线程的同步(同步方法)
线程的同步是保证多线程安全访问竞争资源的一种手段.线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源.什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些 ...
- Java多线程——线程的优先级和生命周期
Java多线程——线程的优先级和生命周期 摘要:本文主要介绍了线程的优先级以及线程有哪些生命周期. 部分内容来自以下博客: https://www.cnblogs.com/sunddenly/p/41 ...
随机推荐
- ios10.2真机调试包,ios升级10.2后需要添加
下载地址: http://download.csdn.net/detail/koktear/9710820 添加地址: finder-应用程序-找到Xcode-右击显示包内容-Contents-Dev ...
- 2、实现不同子网之间的信息交流(互相可以PING通)
一.环境: 二个不同的虚拟子网 VMnet1: 192.168.155.0/24 VMnet8: 192.168.170.0/24 编辑 --> 虚拟网络编辑器 (查看自己的子网,相应修改就行) ...
- Swift和Objective-C混编注意事项
前言 Swift已推出数年,与Objective-C相比Swift的语言机制及使用简易程度上更接地气,大大降低了iOS入门门槛.当然这对新入行的童鞋没来讲,的确算是福音,但对于整个iOS编程从业者来讲 ...
- 手机app软件开发有什么需要注意的细节?
在做手机产品设计的过程中,遇到很多看似很小,且很容易被忽略的问题,正是这些手机应用软件开发小问题,一次次的撩拨用户的耐心,让用户对你的产品心生怨念.刚出道的朋友没有经过实战,对细节注意不多,往往都会遇 ...
- 前端自动化测试 —— TDD环境配置(React+TypeScript)
欢迎讨论与指导:) 前言 TDD -- Test-Drive Development是测试驱动开发的意思,是敏捷开发中的一项核心实践和技术,也是一种测试方法论.TDD的原理是在开发功能代码之前,先编写 ...
- EF Code First 初体验
Code First 顾名思义就是先代码,再由代码生成数据库的开发方式. 废话不多说,直接来一发看看:在VS2010里新建一个空白解决方案,再依次添加两个类库项目:Model.DataAccess和一 ...
- ConcurrentHashMap
ConcurrentHashMap是Java5中新增加的一个线程安全的Map集合,可以用来替代HashTable.对于ConcurrentHashMap是如何提高其效率的,可能大多人只是知道它使用了多 ...
- C-RAN 集中化、协作化、云化、绿色节能(4C)
中国移动C-RAN力拼第4个C:2018年6月外场组网验证 http://www.c114.net ( 2016/11/22 07:41 ) C114讯 11月22日早间消息(子月)2009年,中国移 ...
- Java的基本数据类型与转换
1.1 Java为什么需要保留基本数据类型 http://www.importnew.com/11915.html 基本数据类型对大多数业务相关或网络应用程序没有太大的用处,这些应用一般是采用客户端/ ...
- Ext分页实现(前台与后台)
Ext分页实现(前台与后台)Spring+Mybatis 一.项目背景 关于Ext的分页网上有很多博客都有提到,但是作为Ext新手来说,并不能很容易的在自己的项目中得以应用.因为,大多数教程以及博客基 ...