Java 线程安全
时间:2020-04-17
本文章向大家介绍Java 线程安全,主要包括Java 线程安全使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
参照:https://www.cnblogs.com/lizhangyong/p/8029287.html
一个程序在运行起来时,会转换为进程,通常含有多个线程。
通常情况下,一个进程中的比较耗时的操作(如长循环、文件上传下载、网络资源获取等),往往会采用多线程来解决。
比如,现实生活中,银行取钱问题、火车票多个窗口售票问题等,通常会涉及并发问题,从而需要用到多线程技术。
当进程中有多个并发线程进入一个重要数据的代码块时,在修改数据的过程中,很有可能引发线程安全问题,从而造成数据异常。例如,正常逻辑下,同一个编号的火车票只能售出一次,却由于线程安全问题而被多次售出,从而引起实际业务异常。
接下来,我以售票问题,来演示多线程问题中对核心数据保护的重要性。我们先来看不对多线程数据进行保护时会引发什么样的状况。
public class SellTicket extends Thread { static int tickets = 10; @Override public void run(){ while(tickets > 0){ System.out.println(Thread.currentThread().getName() + "-->售出第 " + tickets + "张票" ); tickets--; }try{ Thread.sleep(100); }catch(InterruptedException e){ e.printStackTrace(); } if(tickets < 0){ System.out.println(Thread.currentThread().getName()+"-->售票结束!"); } } public static void main(String[] args) { SellTicket sell = new SellTicket(); Thread t1=new Thread(sell, "1号窗口"); Thread t2=new Thread(sell, "2号窗口"); Thread t3=new Thread(sell, "3号窗口"); Thread t4=new Thread(sell, "4号窗口"); // t1.setName("1号窗口"); // SellTicket t2 = new SellTicket(); // t2.setName("2号窗口"); // SellTicket t3 = new SellTicket(); // t3.setName("3号窗口"); // SellTicket t4 = new SellTicket(); // t4.setName("4号窗口"); t1.start(); t2.start(); t3.start(); t4.start(); } }
运行结果:
1号窗口-->售出第 10张票 1号窗口-->售出第 9张票 1号窗口-->售出第 8张票 1号窗口-->售出第 7张票 1号窗口-->售出第 6张票 3号窗口-->售出第 6张票 3号窗口-->售出第 4张票 3号窗口-->售出第 3张票 3号窗口-->售出第 2张票 3号窗口-->售出第 1张票 1号窗口-->售出第 5张票 4号窗口-->售票结束! 1号窗口-->售票结束! 3号窗口-->售票结束! 2号窗口-->售票结束!
同一张票会被售出多次,显然不符合实际逻辑。
为了解决上述脏数据的问题,我为大家介绍3种使用比较普遍的三种同步方式。
第一种,同步代码块。
有synchronized关键字修饰的语句块,即为同步代码块。同步代码块会被JVM自动加上内置锁,从而实现同步。
public class SellTicket2 { static int tickets=10; class SellTickets implements Runnable{ @Override public void run() { synchronized(this){ while(tickets > 0){ System.out.println(Thread.currentThread().getName()+" -->售出第 "+tickets+" 张票"); tickets--; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } if(tickets<=0){ System.out.println(Thread.currentThread().getName()+" -->售票结束!"); } } } } public static void main(String[] args) { SellTickets sell = new SellTicket2().new SellTickets(); Thread t1=new Thread(sell, "1号窗口"); Thread t2=new Thread(sell, "2号窗口"); Thread t3=new Thread(sell, "3号窗口"); Thread t4=new Thread(sell, "4号窗口"); t1.start(); t2.start(); t3.start(); t4.start(); } }
运行结果:
1号窗口 -->售出第 10 张票 1号窗口 -->售出第 9 张票 1号窗口 -->售出第 8 张票 1号窗口 -->售出第 7 张票 1号窗口 -->售出第 6 张票 1号窗口 -->售出第 5 张票 1号窗口 -->售出第 4 张票 1号窗口 -->售出第 3 张票 1号窗口 -->售出第 2 张票 1号窗口 -->售出第 1 张票 1号窗口 -->售票结束! 2号窗口 -->售票结束! 4号窗口 -->售票结束! 3号窗口 -->售票结束!
第二种,同步方法 。
即有synchronized关键字修饰的方法。由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
public class SellTicket2 { static int tickets = 10; class SellTickets implements Runnable{ @Override public void run() { while(tickets > 0){ synMethod(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if(tickets <= 0){ System.out.println(Thread.currentThread().getName()+" -->售票结束!"); } } } synchronized void synMethod(){ synchronized(this){ if(tickets <= 0){ return; } System.out.println(Thread.currentThread().getName()+" -->售出第 "+tickets+" 张票"); tickets--; } } } public static void main(String[] args) { SellTickets sell = new SellTicket2().new SellTickets(); Thread t1=new Thread(sell, "1号窗口"); Thread t2=new Thread(sell, "2号窗口"); Thread t3=new Thread(sell, "3号窗口"); Thread t4=new Thread(sell, "4号窗口"); t1.start(); t2.start(); t3.start(); t4.start(); } }
第三种,Lock锁机制。
通过创建Lock对象,采用lock()加锁,采用unlock()解锁,来保护指定代码块。
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class SellTicket2 { static int tickets = 10; class SellTickets implements Runnable{ Lock lock = new ReentrantLock(); @Override public void run() { while(tickets > 0){ try { lock.lock(); if(tickets <= 0){ return; } System.out.println(Thread.currentThread().getName()+" -->售出第 "+tickets+" 张票"); tickets--; } finally{ lock.unlock(); try{ Thread.sleep(100); }catch (InterruptedException e) { e.printStackTrace(); } } if(tickets <= 0){ System.out.println(Thread.currentThread().getName()+" -->售票结束!"); } } } } public static void main(String[] args) { SellTickets sell = new SellTicket2().new SellTickets(); Thread t1=new Thread(sell, "1号窗口"); Thread t2=new Thread(sell, "2号窗口"); Thread t3=new Thread(sell, "3号窗口"); Thread t4=new Thread(sell, "4号窗口"); t1.start(); t2.start(); t3.start(); t4.start(); } }
原文地址:https://www.cnblogs.com/jszfy/p/12720900.html
- 安装serverstatus监控多台服务器状态
- Windows2012搭建我的世界(Minecraft)服务器超简单
- 高效程序员的MacBook工作环境配置
- vsftpd搭建自己的ftp服务器
- Linux一键安装Transmission电影下载到服务器
- centos安装ab工具给网站进行压力测试
- ubuntu16.04安装mongodb教程
- linux压缩解压命令使用
- linux使用wc命令查看文件行数、字母、字节数命令
- 程序员偷偷深爱的 9 个不良编程习惯
- 低级程序员和高级程序员的区别
- Silverlight学习(二)
- Silverlight学习(三)
- ArcGIS for Android学习(一)
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Linux 命令利用scp实现从服务器共享地址上传下载文件、文件夹实例演示,scp命令的参数详解
- Oracle 数据库利用sql语句判断某个表是否是临时表实例演示,达梦数据库查询出所有临时表
- JavaScript 技术篇-一段js代码展示可以随鼠标移动变换样式的卡通人物,动态女生眼睛跟着鼠转动
- PyQt5 图形界面-用Qt Designer来设计UI界面,并转化为python代码运行
- Python 技术篇-python生成html源码功能实现演示,html代码自动生成技巧。列表生成式的灵活应用。
- Python 技术篇-pyHook键盘鼠标监听事件,监测鼠标键盘按键。超简单,几行代码搞定。
- Python 技术篇-用mutagen库提取MP3歌曲图片
- Python 典藏篇-Microsoft Visual C++ 14.0 is required,官方vc++运行库工具一键式解决!
- Python 技术篇-邮件写入html代码,邮件发送表格,邮件发送超链接,邮件发送网络图片
- 面经手册 · 第11篇《StringBuilder 比 String 快?空嘴白牙的,证据呢!》
- domReady的理解
- Map集合排序
- Chrome 技术篇-一台电脑设置多个独立chrome方法。chrome独立多开技术。
- 023.Ubuntu常见个性化配置
- 快速学习-ElasticJob的FAQ