先举个经典的例子说明线程安全问题:
三个窗口卖100张票
class TicketThread implements Runnable{ private int total=100; public void run() { while (true){ if(total>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"卖了第"+total--+"张票"); } } } } public class TiketTest { public static void main(String[] args) { TicketThread ticket = new TicketThread(); Thread t1 = new Thread(ticket,"窗口1"); Thread t2 = new Thread(ticket,"窗口2"); Thread t3 = new Thread(ticket,"窗口3"); t1.start(); t2.start(); t3.start(); } }
明显看到票被卖到了-1张....这个时候就需要线程锁了,当一个窗口卖票的时候,其他窗口需要排队等待。
格式:
synchronized(任意对象){需要实现线程安全的代码}
上代码
class TicketThread implements Runnable{ private int total=100; public void run() { while (true){ synchronized (this){ if(total>0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"卖了第"+total--+"张票"); } } } } } public class TiketTest { public static void main(String[] args) { TicketThread ticket = new TicketThread(); Thread t1 = new Thread(ticket,"窗口1"); Thread t2 = new Thread(ticket,"窗口2"); Thread t3 = new Thread(ticket,"窗口3"); t1.start(); t2.start(); t3.start(); } }
结果完美,需要注意的是所有线程需要上同一把锁。
看下面的例子
public class TiketTest { public static void main(String[] args) { TicketThread ticket1 = new TicketThread(); TicketThread ticket2 = new TicketThread(); TicketThread ticket3 = new TicketThread(); Thread t1 = new Thread(ticket1,"窗口1"); Thread t2 = new Thread(ticket2,"窗口2"); Thread t3 = new Thread(ticket3,"窗口3"); t1.start(); t2.start(); t3.start(); } }
1,2,3窗口同时卖了最后一张票,因为我们synchronized(this),这个锁this是当前对象,我创建了三个不同的对象,这个时候必然要出错。所以我们需要唯一的对象来加锁,所以利用反射拿到类的class对象进行加锁是最常用的。如下:
class TicketThread implements Runnable{ private static int total=100; public void run() { while (true){ synchronized (TicketThread.class){ if(total>0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"卖了第"+total--+"张票"); } } } } } public class TiketTest { public static void main(String[] args) { TicketThread ticket1 = new TicketThread(); TicketThread ticket2 = new TicketThread(); TicketThread ticket3 = new TicketThread(); Thread t1 = new Thread(ticket1,"窗口1"); Thread t2 = new Thread(ticket2,"窗口2"); Thread t3 = new Thread(ticket3,"窗口3"); t1.start(); t2.start(); t3.start(); } }
这样就能保证多个对象是创建的同一把锁
其实和synchronized代码块是一样的,只不过是将代码块中的方法抽象出来
普通方法就相当于synchronized(this)
class TicketThread implements Runnable{ private int total=100; public void run() { while (true) { sellTicket(); } } public synchronized void sellTicket(){ if(total>0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"卖了第"+total--+"张票"); } } } public class TiketTest { public static void main(String[] args) { TicketThread ticket1 = new TicketThread(); Thread t1 = new Thread(ticket1,"窗口1"); Thread t2 = new Thread(ticket1,"窗口2"); Thread t3 = new Thread(ticket1,"窗口3"); t1.start(); t2.start(); t3.start(); } }
静态方法就相当于synchronized(类.class)
class TicketThread implements Runnable{ private static int total=100; public void run() { while (true) { sellTicket(); } } public static synchronized void sellTicket(){ if(total>0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"卖了第"+total--+"张票"); } } } public class TiketTest { public static void main(String[] args) { TicketThread ticket1 = new TicketThread(); TicketThread ticket2 = new TicketThread(); TicketThread ticket3 = new TicketThread(); Thread t1 = new Thread(ticket1,"窗口1"); Thread t2 = new Thread(ticket2,"窗口2"); Thread t3 = new Thread(ticket3,"窗口3"); t1.start(); t2.start(); t3.start(); } }
Lock的三个常用方法,
lock()获取锁。
tryLock()尝试获取锁,成功返回true,失败返回false,不阻塞
unlock()释放锁。
例子:
class MyArray{ private String strArr[]={"A","B","",""}; private int index=2; private ReentrantLock lock = new ReentrantLock(); public void add(String str){ lock.lock(); try { strArr[index] = str; index++; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"添加了"+str); }finally { lock.unlock(); } } public String[] getStrArr(){ return strArr; } } public class ArrayTest { public static void main(String[] args) throws InterruptedException { final MyArray myArray = new MyArray(); Runnable run1 = new Runnable() { public void run() { myArray.add("hello"); } }; Runnable run2 = new Runnable() { public void run() { myArray.add("world"); } }; Thread t1 = new Thread(run1); Thread t2 = new Thread(run2); t1.start(); t2.start(); t1.join(); t2.join(); String[] strings = myArray.getStrArr(); for (String string : strings) { System.out.println(string); } } }
注意一定要释放锁,所以一般会将释放锁放在finally保证锁的释放
文章浏览阅读5.9k次,点赞5次,收藏16次。leaflet通过WFS服务加载geoserver 矢量数据1.前言2.从geoserver获得geojson数据3.geoserver跨域配置4.根据请求结果生成layer5.完整代码1.前言leaflet默认支持的服务只有WMS,因此不能加载WFS数据,但是leaflet提供了另一个方法geoJson,它的作用是从一个geojson文件中加载地图,所以利用leaflet加载WFS数据的一个..._leaflet geoserver wfs 方式
文章浏览阅读937次。开发工具与关键技术:VS,MVC作者:陈梅撰写时间:2019年6月2 日所有代码来源与老师教学这次分享一个好玩的自定义动画效果,这次还是用jQuery做出来的小功能。这次我们先直接看最后已经布局好的效果。把所想写的内容填写到p标签中,给到p标签的动画功能是,页面已执行时,p标签的内容就会渐渐消失。在给一个紫色的div盒子,这个盒子要实现四种动画效果,所以给这四个动画效果一个下拉框,选择..._使用animate方法制作任意动画是什么意思
文章浏览阅读1k次。怎样在MongoDB实现mysql show variables like 'xx';例如:1.查看所有参数值:C:\Users\duansf>mongoMongoDB shell version: 2.6..._查看mongodb 默认参数值
文章浏览阅读863次。蚁群算法求解旅行商问题完整的代码,方可运行;可提供运行操作视频!适合小白!
文章浏览阅读1.9w次,点赞6次,收藏39次。物联网数据处理技术的基本概念物联网数据的特点海量 动态 多态 关联从无线传感器网络TinyDB数据库结构中可以清晰地看到物联网数据“海量、动态、多态、关联”的特点物联网中的数据、信息与知识物联网数据处理关键技术数据存储 数据融合 数据挖掘 智能决策物联网与云计算云计算产生的背景云计算的分类IaaS—基础设施即服务,只涉及到租用硬件,是一种..._物联网数据处理技术
文章浏览阅读4.8k次。很多朋友改完win10系统就找不到打印机设备,无法设置默认打印机,今天来解析这个问题!01进入设置界面通常,对于已经启动了并连接到了网络的打印机,会很容易被系统识别到,只不过需要确保打印机和电脑是连接的同一个网络。点击开始菜单,进入设置界面。选择设备。02添加打印机和扫描仪选择打印机和扫描仪,点击添加打印机或扫描仪。系统将会自动搜索识别,并将搜索到的设备罗列出来。接着,找到并点击您想要添加的打印机..._w10打印机在哪里找
文章浏览阅读2.1k次。匿名用户1级2016-09-11 回答其实吧,学习C语言是以后从事软件设计的一个基础。任何领域都需要长时间的投入才有结果,你现在学习了C语言,再学习其他语言的时候就比较上手了。在软件设计中:学习一门语言仅仅是第一阶段:如果你基本掌握了一门语言,那么再想深入学习的话就需要把所有C语言的相关的库函数弄懂,并熟练掌握一个开发平台(如最基础的TC)。这是第二阶段下一阶段你就需要继续学习不同的操作系统所提供..._c语言入门后怎么深入
文章浏览阅读672次。如果你正准备从头开始制作一个新的应用,那么React Native会是个非常好的选择。但如果你只想给现有的原生应用中添加一两个视图或是业务流程,React Native也同样不在话下。只需简单几步,你就可以给原有应用加上新的基于React Native的特性、画面和视图等。https://zjqian.github.io/2017/05/03/rn-integration-iosNative/_ios原生项目嵌入reactnative 模块
文章浏览阅读608次。本次终于写到了第五章了,前面四章节,我们从一个全新的 umi3 的ant design pro 模板开始着手,我们以一个初始者要用它的思想介入,逐步走了新增路由、cssmodules、国际化语言切换、使用mock数据进行快速开发、联调正式接口、初始化配置、登录修改、接口文件提取等等。这次到第五章了,我们暂时不做新的改变,我们来把之前写的一些杂项收拾收拾,比如,清除一些不需要的代码,规范一些东西,让我们的项目成为我们的快速开发模板。_umi 去除代码的lo
文章浏览阅读1.2k次。Android 源码编译文件中语法记录_android shell脚本语法 :>
文章浏览阅读4.2k次,点赞12次,收藏72次。1.概述Linux系统上的Video设备多种多样,如通过Camera Host控制器接口连接的摄像头,通过USB总线连接的摄像头等。为了兼容更多的硬件,Linux内核抽象了V4L2(Video for Linux Two)子系统。V4L2子系统是Linux内核中关于Video(视频)设备的API接口,是V4L(Video for Linux)子系统的升级版本。V4L2子系统向上为虚拟文件系统提供了统一的接口,应用程序可通过虚拟文件系统访问Video设备。V4L2子系统向下给Video设备提供接口,同时管理_v4l2_subdev_call
文章浏览阅读1w次。使用场景:因为在公司机房中的服务器我们在使用需要对他做一些类似于初始化的配置,分别是三个,——》第一个是配置服务器的ILO地址,这个是我们通过网络打开一个Web页面对服务器进行一些操作;——》第二个是对管理用户的密码进行修改,这个是因为不同的服务器初始的管理员的密码也许是不一样的,我们将其修改为统一的方便记忆也方便管理;——》第三个就是开启服务器的半虚拟化功能,这个是我们的公司的也许需要服..._浪潮服务器修改管理口密码