Java 面试题:如何保证集合是线程安全的? ConcurrentHashMap 如何实现高效地线程安全?
在多线程编程中,保证集合的线程安全是一个常见而又重要的问题。线程安全意味着多个线程可以同时访问集合而不会导致数据不一致或程序崩溃。在 Java 中,确保集合线程安全的方法有多种,包括使用同步包装类、锁机制以及并发集合类。
最简单的方法是使用
Collections.synchronizedXXX
方法来包装集合,例如Collections.synchronizedList
和Collections.synchronizedMap
。然而,这种方式的性能较低,因为它在每个操作上都添加了同步锁。为了解决性能问题,Java 提供了一系列并发集合类,如
ConcurrentHashMap
、CopyOnWriteArrayList
等。这些类通过细粒度锁和无锁算法来提高并发性能。特别是ConcurrentHashMap
,它通过分段锁(Segment Locking)机制,将整个哈希表分成多个段,每个段独立加锁,从而实现更高效的并发访问。理解这些线程安全的实现方法和
ConcurrentHashMap
的高效性,不仅有助于你在多线程编程中写出更安全和高效的代码,还能在面试中展示出对 Java 并发编程的深入理解。
文章目录
- 1、面试问题
- 2、问题分析
- 3、典型回答
- 3.1、传统集合框架的线程安全支持
- 3.2、并发包(java.util.concurrent)的线程安全集合
- 3.3、ConcurrentHashMap的高效线程安全实现
- 4、问题深入
- 4.1、解释传统集合框架中同步容器和同步包装器的局限性
- 4.2、讨论并发包中的各种线程安全集合及其适用场景
- 4.3、分析ConcurrentHashMap在Java 7和Java 8中的实现差异
- 4.4、解释CAS操作及其在ConcurrentHashMap中的应用
- 4.5、讨论ConcurrentHashMap的树化结构及其优势
1、面试问题
今天的面试问题:Java 如何保证集合是线程安全的? ConcurrentHashMap 如何实现高效地线程安全?
2、问题分析
这个问题主要考察以下几个关键点:
- Java集合框架的线程安全支持:了解Java集合框架中如何保证线程安全。
- 同步包装器的使用:掌握
Collections.synchronizedXXX
方法的使用和局限性。 - 并发包(java.util.concurrent)的线程安全集合:了解并发包中的各种线程安全集合类。
- ConcurrentHashMap的实现细节:深入理解ConcurrentHashMap如何通过分段锁等机制实现高效的线程安全。
这个问题不仅考察基础知识,还涉及Java并发编程的实践和高级特性,是评估Java开发者技能的一个重要方面。
3、典型回答
Java 提供了多种方式来保证集合的线程安全,具体包括:
3.1、传统集合框架的线程安全支持
- 同步容器:如
Hashtable
,其所有方法都被synchronized
修饰,确保线程安全。 - 同步包装器:使用
Collections.synchronizedXXX
方法,可以将非线程安全的集合包装为线程安全的集合。
示例:
Map<Integer, String> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
List<Integer> synchronizedList = Collections.synchronizedList(new ArrayList<>());
3.2、并发包(java.util.concurrent)的线程安全集合
- 并发容器:并发包提供了多种线程安全的集合类,如
ConcurrentHashMap
、CopyOnWriteArrayList
、ConcurrentLinkedQueue
等。 - 线程安全队列:如
ArrayBlockingQueue
、SynchronousQueue
等。 - 线程安全的有序容器:如
ConcurrentSkipListMap
和ConcurrentSkipListSet
。
示例:
ConcurrentHashMap<Integer, String> concurrentHashMap = new ConcurrentHashMap<>();
CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
ArrayBlockingQueue<Integer> arrayBlockingQueue = new ArrayBlockingQueue<>(10);
3.3、ConcurrentHashMap的高效线程安全实现
ConcurrentHashMap 在设计上采用了一些高级机制来实现高效的线程安全:
- 分段锁(Segmented Locking):早期版本(Java 7及之前)使用分段锁技术,将整个Map分成多个段,每个段独立加锁,提高并发性能。
- CAS(Compare-And-Swap)操作:Java 8中采用CAS操作(无锁算法)来更新某些字段,如计数器,减少锁的开销。
- 分离锁:使用不同类型的锁来保护不同的数据结构。例如,在Java 8中,使用
ReentrantLock
和synchronized
关键字结合来实现高效并发控制。 - 分布式桶:使用多个桶来存储数据,每个桶都有自己的锁,减少锁的竞争。
- 树化结构:当单个桶中的元素数量过多时,采用红黑树结构来替代链表,提高查询效率。
示例:
ConcurrentHashMap<Integer, String> concurrentHashMap = new ConcurrentHashMap<>();
concurrentHashMap.put(1, "value1");
concurrentHashMap.put(2, "value2");
4、问题深入
4.1、解释传统集合框架中同步容器和同步包装器的局限性
同步容器(Synchronized Containers)
- 定义:同步容器如
Hashtable
和Vector
,所有方法都使用synchronized
关键字修饰,以确保线程安全。 - 局限性:
- 性能瓶颈:由于每个操作都需要获取锁,在高并发环境下性能较低。
- 竞争激烈:当多个线程频繁访问和修改集合时,锁竞争会变得非常激烈,导致系统性能下降。
示例:
Hashtable<Integer, String> hashtable = new Hashtable<>();
hashtable.put(1, "value1");
hashtable.put(2, "value2");
同步包装器(Synchronized Wrappers)
- 定义:使用
Collections.synchronizedXXX
方法将非线程安全的集合包装成线程安全的集合。 - 局限性:
- 粗粒度锁:整个集合只有一个锁,所有操作都需要获取同一个锁,导致并发性能较低。
- 复杂操作的额外同步:对于迭代等复合操作,仍然需要手动同步以确保线程安全。
示例:
Map<Integer, String> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
synchronized (synchronizedMap) {for (Map.Entry<Integer, String> entry : synchronizedMap.entrySet()) {System.out.println(entry.getKey() + ": " + entry.getValue());}
}
4.2、讨论并发包中的各种线程安全集合及其适用场景
ConcurrentHashMap
- 定义:高效的线程安全哈希表,允许多个线程并发读写。
- 适用场景:高并发环境下的键值对存储和快速查找。
- 示例:
ConcurrentHashMap<Integer, String> concurrentHashMap = new ConcurrentHashMap<>();
concurrentHashMap.put(1, "value1");
concurrentHashMap.put(2, "value2");
CopyOnWriteArrayList
- 定义:适用于读多写少的场景,写操作会创建数组的副本。
- 适用场景:读多写少的应用,如缓存、配置数据。
- 示例:
CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
copyOnWriteArrayList.add(1);
copyOnWriteArrayList.add(2);
System.out.println(copyOnWriteArrayList.get(0));
ConcurrentLinkedQueue
- 定义:无界线程安全队列,基于无锁算法实现。
- 适用场景:高并发环境下的任务调度和消息传递。
- 示例:
ConcurrentLinkedQueue<Integer> concurrentLinkedQueue = new ConcurrentLinkedQueue<>();
concurrentLinkedQueue.add(1);
concurrentLinkedQueue.add(2);
System.out.println(concurrentLinkedQueue.poll());
ArrayBlockingQueue
- 定义:有界阻塞队列,支持线程间的通信和同步。
- 适用场景:生产者-消费者模型,实现线程间的任务调度。
- 示例:
ArrayBlockingQueue<Integer> arrayBlockingQueue = new ArrayBlockingQueue<>(10);
arrayBlockingQueue.put(1);
arrayBlockingQueue.put(2);
System.out.println(arrayBlockingQueue.take());
4.3、分析ConcurrentHashMap在Java 7和Java 8中的实现差异
Java 7中的实现
- 分段锁(Segmented Locking):将整个Map分成多个段(Segment),每个段独立加锁,提高并发性能。
- 工作原理:每个Segment内部使用类似于
Hashtable
的实现,但不同Segment之间可以并行操作。
示例:
// Java 7中的ConcurrentHashMap
ConcurrentHashMap<Integer, String> concurrentHashMap = new ConcurrentHashMap<>(16, 0.75f, 16);
Java 8中的实现
- 无锁和CAS操作:引入CAS操作(无锁算法)来更新某些字段,减少锁的开销。
- 分离锁:使用不同类型的锁来保护不同的数据结构,结合
ReentrantLock
和synchronized
关键字实现高效并发控制。 - 树化结构:当单个桶中的元素数量超过阈值时,采用红黑树结构来替代链表,提高查询效率。
示例:
// Java 8中的ConcurrentHashMap
ConcurrentHashMap<Integer, String> concurrentHashMap = new ConcurrentHashMap<>();
concurrentHashMap.put(1, "value1");
concurrentHashMap.put(2, "value2");
4.4、解释CAS操作及其在ConcurrentHashMap中的应用
CAS操作
- 定义:比较并交换(Compare-And-Swap),一种无锁的并发操作,通过硬件指令实现原子操作。
- 原理:CAS操作包括三个操作数:内存位置、预期旧值和新值。只有当内存位置的当前值与预期旧值相等时,才会将新值写入内存位置。
在ConcurrentHashMap中的应用
- 计数器更新:使用CAS操作来更新计数器,避免加锁带来的性能开销。
- 节点插入和删除:在节点插入和删除时,使用CAS操作来确保原子性,减少锁的使用。
示例:
import java.util.concurrent.atomic.AtomicInteger;public class CASExample {private final AtomicInteger count = new AtomicInteger(0);public void increment() {int oldValue;int newValue;do {oldValue = count.get();newValue = oldValue + 1;} while (!count.compareAndSet(oldValue, newValue));}public int getCount() {return count.get();}
}
4.5、讨论ConcurrentHashMap的树化结构及其优势
树化结构
- 定义:当单个桶中的元素数量超过阈值时,将链表转换为红黑树,提高查询和更新操作的效率。
- 触发条件:默认情况下,当桶中的元素数量超过8个时进行树化。
优势
- 提高性能:链表长度增加会导致查询性能下降,而红黑树在最坏情况下的查询复杂度为O(log n),显著提高了查询性能。
- 减少冲突:红黑树结构减少了哈希冲突带来的性能问题。
相关文章:
![](https://www.ngui.cc/images/no-images.jpg)
Java 面试题:如何保证集合是线程安全的? ConcurrentHashMap 如何实现高效地线程安全?
在多线程编程中,保证集合的线程安全是一个常见而又重要的问题。线程安全意味着多个线程可以同时访问集合而不会导致数据不一致或程序崩溃。在 Java 中,确保集合线程安全的方法有多种,包括使用同步包装类、锁机制以及并发集合类。 最简单的方法…...
![](https://img-blog.csdnimg.cn/img_convert/4ed96c95c7344e0a2445a53ccfbc26d6.png)
打工人的PPT救星来了!用这款AI工具,10秒生成您的专属PPT
今天帮同事解决了一个代码合并的问题。其实问题不复杂,要把1的代码合到2的位置: 这个处理方式其实很简单,使用 “git cherry-pick hash值” 就可以。 同事直接对我赞许有加,不曾想被领导看到了,对我说了一句ÿ…...
![](https://www.ngui.cc/images/no-images.jpg)
GIT 合拼
合拼有多种方式: 1)合拼分支: git merge [source-branch] 2)合拼提交 : git cherry-pick [commit-hash] 3)合拼单个文件: git checkout [source-branch] – [file] 以上合拼,比如将分…...
![](https://www.ngui.cc/images/no-images.jpg)
利用 Python 和 AI 技术制作智能问答机器人
利用 Python 和 AI 技术制作智能问答机器人 引言 在人工智能的浪潮下,智能问答机器人成为了一种非常实用的技术。它们能够处理大量的查询,提供即时的反馈,并且可以通过机器学习技术不断优化自身的性能。本文将介绍如何使用 Python 来开发一…...
![](https://www.ngui.cc/images/no-images.jpg)
electron系列(一)调用dll
用electron的目的,其实很简单。就是web架构要直接使用前端电脑的资源,但是浏览器限制了使用,所以用electron来达到这个目的。其中调用dll是一个非常基本的操作。 安装 ffi-napi 和 ref-napi 包: npm install ffi-napi ref-napi main.js&…...
![](https://img-blog.csdnimg.cn/direct/e82d4f1cd63841b096def2d887fdf384.gif?#pic_center)
VUE3实现个人网站模板源码
文章目录 1.设计来源1.1 网站首页页面1.2 个人工具页面1.3 个人日志页面1.4 个人相册页面1.5 给我留言页面 2.效果和源码2.1 动态效果2.2 目录结构 源码下载万套模板,程序开发,在线开发,在线沟通 作者:xcLeigh 文章地址࿱…...
![](https://img-blog.csdnimg.cn/direct/a4ce521180a5435e85f49f0e6ba9e009.png)
C语言 | Leetcode C语言题解之第162题寻找峰值
题目: 题解: int findPeakElement(int* nums, int numsSize) {int ls_max0;for(int i1;i<numsSize;i){if(nums[ls_max]>nums[i]);else{ls_maxi;}}return ls_max; }...
![](https://www.ngui.cc/images/no-images.jpg)
利用pickle保存和加载对象
使用 pickle.dump 保存下来的文件可以使用 pickle.load 打开和读取。以下是一个示例,展示了如何使用 pickle 模块保存和加载对象: 保存对象 import pickle# 假设有一个对象 obj obj {"key": "value"}# 将对象保存到文件 with ope…...
![](https://img-blog.csdnimg.cn/direct/6d75ea3535e1478bac2df7ce6ebc4b88.jpeg)
定制汽车霍尔传感器
磁电效应霍尔传感器、饱和霍尔传感器、非线性霍尔传感器 霍尔传感器原理 霍尔传感器的工作原理基于霍尔效应,即当一块通有电流的金属或半导体薄片垂直地放在磁场中时,薄片的两端会产生电位差。这种现象称为霍尔效应,两端具有的电位差值称为…...
![](https://img-blog.csdnimg.cn/direct/a2b2f85b50134deebe9b053692534dd4.png)
【2024最新华为OD-C/D卷试题汇总】[支持在线评测] LYA的巡演(100分) - 三语言AC题解(Python/Java/Cpp)
🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 …...
![](https://www.ngui.cc/images/no-images.jpg)
ChatGPT 简介
ChatGPT 是一种基于大型语言模型的对话系统,由 OpenAI 开发。它的核心是一个深度学习模型,使用了 GPT(Generative Pre-trained Transformer)架构。以下是 ChatGPT 的原理和工作机制的详细介绍: ### GPT 架构 1. **Tr…...
![](https://img-blog.csdnimg.cn/59ba0d8c8a3a40ea9e7d74566afac93a.png)
大数据实训室建设可行性报告
一、建设大数据实训室的背景与意义 随着信息技术的飞速发展,大数据已成为推动社会进步和经济发展的重要力量。中高职院校作为技能型人才培养的摇篮,承担着为社会输送大数据领域高素质、高技能人才的重要任务。因此,建设大数据实训室…...
![](https://www.ngui.cc/images/no-images.jpg)
学懂C#编程:让函数返回 多个返回值 的几种常用技术
1. 使用 out 或 ref 参数 out 和 ref 参数允许方法修改传入变量的值,并通过它们“返回”多个值。ref 需要变量事先初始化,而 out 不要求。 public void GetValues(out int val1, out string val2) {val1 10;val2 "Hello"; }// 使用示例 int…...
![](https://img-blog.csdnimg.cn/img_convert/8fb5eb3d7bd79d5e8a7e54589af72728.jpeg)
蔚来汽车AI算法工程师,如何理解注意力?
大家好啊,我是董董灿。 今天分享一个上海蔚来汽车的AI算法岗位面试经验总结帖,面试岗位为算法工程师。 这次面试提到的问题,除了与实习相关内容和反问之外,面试官总共问了8个问题,主要集中在深度学习基础概念的理解上…...
![](https://img-blog.csdnimg.cn/direct/56cfe512ab684b6381915d023953e167.png)
信创适配评测
概叙 信创科普参考:全面国产化之路-信创-CSDN博客 有必要再解释一下两个名词“28N”,“79号文件”,因为“28N”指定了由政府牵头从各领域开启国产化的基调,而“79号文件”则指定了国产化的截止日期2027年。 信创的本质是实现中国信…...
![](https://www.ngui.cc/images/no-images.jpg)
【Qt6.3 基础教程 04】探索Qt项目结构和配置文件
文章目录 前言Qt项目的基本结构配置文件:.pro文件基本构成示例.pro文件: qmake和构建过程步骤简述: 修改项目设置结论 前言 当你开始使用Qt进行开发时,理解项目结构和配置文件的作用是至关重要的。这篇博文将带你深入了解Qt项目的…...
![](https://img-blog.csdnimg.cn/direct/39ed29e8b14c44b7841a53d280f4925b.png)
SpringBoot测试实践
测试按照粒度可分为3层: 单元测试:单元测试(Unit Testing)又称为模块测试 ,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中…...
![](https://www.ngui.cc/images/no-images.jpg)
Flask-OAuthlib
Flask-OAuthlib库教程 Flask-OAuthlib 是一个为 Flask 应用提供 OAuth1 和 OAuth2 支持的库。它允许开发者轻松地集成第三方 OAuth 服务,或者构建自己的 OAuth 提供者服务。 官方文档链接 Flask-OAuthlib官方文档 架构概述 Flask-OAuthlib 的主要组件包括&…...
![](https://img-blog.csdnimg.cn/direct/aec6cce556914ef7985b11374afabd17.png)
树和森林.
目录 一、树 1.1树的存储结构 1.1.1双亲表示法 1.1.2孩子链表 1.1.3孩子兄弟表示法 1.2树与二叉树的转换 1.2.1将树转换成二叉树: 1.2.2将二叉树转换成树 二、森林 2.1森林与二叉树的转换 2.1.1将森林转换成二叉树 2.1.2二叉树转换成森林 三、树和森林的…...
![](https://img-blog.csdnimg.cn/direct/42eda82cdfae4be58029891a7afaf8bc.png)
ubuntu下同时安装和使用不同版本的库 librealsense
apt 安装的最新版本在/usr 源码安装的旧版本在/usr/local set(realsense2_DIR /usr/local/) find_package(realsense2 2.50.0 REQUIRED) message( "\n\n ${realsense2_INCLUDE_DIR} ${realsense2_VERSION} RealSense SDK 2.0 is FINDINGING, please install it from…...
![](https://www.ngui.cc/images/no-images.jpg)
openEuler操作系统下静默安装Oracle19c
在openEuler-23.09上安装Oracle19c,创建非容器数据库实例(含静默安装) 操作系统版本 openEuler-23.09-x86_64-dvd.iso ,安装步骤此处省略。。。 最常用且直接的方法来查看openEuler的版本号是查看/etc/os-release文件 [root@openEuler ~]$ cat /etc/os-release NAME="…...
![](https://www.ngui.cc/images/no-images.jpg)
Linux CPU常见命令行详解
在Linux系统中,命令行是管理和监控系统资源的重要工具。特别是当我们需要了解CPU的状态、性能和利用率时,一系列命令行工具就显得尤为重要。本文将详细介绍Linux中与CPU相关的常见命令行工具及其使用方法,帮助大家更好地理解和利用这些工具来…...
![](https://www.ngui.cc/images/no-images.jpg)
防止更新或保存 Laravel 模型
例如,创建模型后,我不希望任何人能够再次更新该记录。相反,它应该被全新的记录覆盖并存档。 这是一个简单的特征,您可以在模型上使用它来禁用更新: trait PreventsUpdating {public static function bootPreventsUpd…...
![](https://www.ngui.cc/images/no-images.jpg)
Cadence:Conformal系列形式验证工具
Conformal 工具最早由Verplex Systems开发。Verplex是一家专注于形式验证工具开发的公司,其核心产品是Conformal等效性检查工具。由于其技术的先进性和市场需求,Verplex的 Conformal工具迅速在半导体行业内获得了认可。 2003 年,Cadence Desi…...
![](https://img-blog.csdnimg.cn/img_convert/44e29c2db0c3f9e8dae9ad498b8a95f0.png)
一般人不要学Python?一般人怎么学Python!!
关于“建议一般人真的不要学Python”这一观点,我认为这是一个过于绝对的说法。实际上,Python作为一种流行的编程语言,具有许多优点,适合不同背景和需求的人学习。以下是一些反驳这一观点的理由: 易于学习和理解&#x…...
![](https://www.ngui.cc/images/no-images.jpg)
微服务架构中间件安装部署
微服务架构中间件安装部署 jdk安装 安装包jdk-8u144-linux-x64.tar.gz 先检查系统原版本的jdk并卸载 rpm -qa | grep java 显示信息如下: tzdata-java-2014g-1.el6.noarch java-1.6.0-openjdk-1.6.0.0-11.1.13.4.el6.x86_64 java-1.7.0-openjdk-1.7.0.65-2.5.1.2.…...
![](https://img-blog.csdnimg.cn/direct/285f8db216c84b77b0eae9b409136d50.jpeg)
车辆数据的提取、定位和融合(其一 共十二篇)
第一篇: System Introduction 第二篇:State of the Art 第三篇:localization 第四篇:Submapping and temporal weighting 第五篇:Mapping of Point-shaped landmark data 第六篇:Clustering of landma…...
![](https://www.ngui.cc/images/no-images.jpg)
Vue3组件通信全解析:利用props、emit、provide/inject跨层级传递数据,expose与ref实现父子组件方法调用
文章目录 一、父组件数据传递N个层级的子组件vue3 provide 与 injectA组件名称 app.vueB组件名称 provideB.vueC组件名称 provideCSetup.vue 二、使用v-model指令实现父子组件的双向绑定父组件名称 app.vue子组件名称 v-modelSetup.vue 三、父组件props向子组件传值子组件 prop…...
![](https://img-blog.csdnimg.cn/direct/f061422bd77a499297054471585d20f3.png#pic_center)
华为---OSPF被动接口配置(四)
9.4 OSPF被动接口配置 9.4.1 原理概述 OSPF被动接口也称抑制接口,成为被动接口后,将不会接收和发送OSPF报文。如果要使OSPF路由信息不被某一网络中的路由器获得且使本地路由器不接收网络中其他路由器发布的路由更新信息,即已运行在OSPF协议…...
![](https://www.ngui.cc/images/no-images.jpg)
前端将Markdown文本转换为富文本显示/编辑,并保存为word文件
参考:https://www.wangeditor.com/ https://blog.csdn.net/weixin_43797577/article/details/138854324 插件: markdown-it traptitech/markdown-it-katex markdown-it-link-attributes highlight.js wangeditor/editor wangeditor/editor-for-vue html…...
![](/images/no-images.jpg)
建设导航网站费用/网页设计html代码大全
Perl命令行应用介绍Perl语言中有很多Perl命令行参数.通过它们,我们有机会写出更简单的程序,在这篇文章里我们来了解一些常用的参数。第一部分:SafetyNetOptions安全网参数在使用Perl尝试一些聪明(或stupid)的想法时,错误难免会发生.有经验的Perl程序员常…...
![](http://upload-images.jianshu.io/upload_images/1501971-91e6c1164e13c3ee.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
企业网站公示怎么做/青岛谷歌优化公司
最近在做番茄钟的功能。首先简单介绍一下番茄钟吧,就是25分钟工作番茄工作法。先说一下** 番茄工作法 **: 番茄工作法是简单易行的时间管理方法,是由弗朗西斯科西里洛于1992年创立的一种相对于GTD更微观的时间管理方法。 使用番茄工作法&…...
![](/images/no-images.jpg)
图片下载 wordpress/百度seo最成功的优化
假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。 给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 …...
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
分类信息网站开发报价/哈尔滨seo优化培训
随着Internet的飞速发展,W3C成员意识到必须找到一种办法将数据和Web的表现方式分离出来,于是XML诞生了。当今的XML已经成为IT领域各个数据(特别是文档)的首选格式。由于它具有标记不同字段的能力,因此使搜索变得更简单…...
![](http://img5.douban.com/lpic/s27236377.jpg)
网站ico在后台哪里找到/怎么关键词优化网站
在软件开发设计中我们经常会面对业务分析,提取领域问题,从而实现软件架构设计。关于 软件架构设计Martin Fowler在2004出版的《企业应用架构模式》中 概括了四种方式的架构模式。它们分别为事务性脚本,表驱动模式,活动记录模式&am…...
网站的ftp地址是什么/搜索引擎优化seo应用
HydroCMS定位于您的私人专属资料管理系统。 下一个功能将是:目录的访问权限设置。目前是根据文件的类型进行权限设计的,比如jpg格式的允许4级权限访问,dwg只能是3级才能访问……。这样不够好,应该按照文件夹进行权限设置ÿ…...