这样真的解决问题了吗,这样虽然只要有用户访问过文件a,那另一个用户想访问文件a,也会从fileName2Data中拿数据,然后也不会引起死循环。可是,如果你觉得这样就已经完了,那你把多线程也想的太简单了,骚年!你会发现,1000个用户首次访问同一个文件的时候,居然读取了1000次文件(这是最极端的,可能只有几百)。What the fuckin hell!!!难道代码错了吗,难道我就这样过我的一生!好好分析下。Servlet是多线程的,那么
IntelliJ IDEA 的缓存和索引主要是用来加快文件查询,从而加快各种查找、代码提示等操作的速度。但是,IntelliJ IDEA 的索引和缓存并不是一直会良好地支持 IntelliJ IDEA 的,这某些特殊条件下,IntelliJ IDEA 的缓存和索引文件也是会损坏的,比如:断电、蓝屏引起的强制关机,当你重新打开 IntelliJ IDEA,基本上百分八十的可能 IntelliJ IDEA 都会报各种莫名其妙错误,甚至项目打不开,IntelliJ IDEA 主题还原成默认状态。也有一些即使没有断电、蓝屏,也会有莫名奇怪的问题的时候,也很有可能是 IntelliJ IDEA 缓存和索引出问题,这种情况还不少
清除缓存和索引
IntelliJ IDEA 已经自带提供清除缓存、索引的路口
一般建议点击 Invalidate and Restart,这样会比较干净。但是有一个需要提醒的是,如上图红圈标注的地方:清除索引和缓存会使得 IntelliJ IDEA 的 Local History 丢失,所以如果你项目没有加入到版本控制,而你又需要你项目文件的历史更改记录,那你最好备份下你的 LocalHistory 目录。目录地址在:C:\Users\当前登录的系统用户名\.IntelliJIdea14\system\LocalHistory 建议使用硬盘的全文搜索,这样效率更高
通过上面方式清除缓存、索引本质也就是去删除 C 盘下的 system 目录下的对应的文件而已,所以如果你不用上述方法也可以删除整个 system。当 IntelliJ IDEA 再次启动项目的时候会重新创建新的 system 目录以及对应项目缓存和索引
如果你遇到了因为索引、缓存坏了以至于项目打不开,那也建议你可以直接删除 system 目录,一般这样都可以很好地解决你的问题
多线程使用的主要目的在于:
1、吞吐量:你做WEB,容器帮你做了多线程,但是他只能帮你做请求层面的。简单的说,可能就是一个请求一个线程。或多个请求一个线程。如果是单线程,那同时只能处理一个用户的请求。
2、伸缩性:也就是说,你可以通过增加CPU核数来提升性能。如果是单线程,那程序执行到死也就利用了单核,肯定没办法通过增加CPU核数来提升性能。鉴于你是做WEB的,第1点可能你几乎不涉及。那这里我就讲第二点吧。--举个简单的例子:假设有个请求,这个请求服务端的处理需要执行3个很缓慢的IO操作(比如数据库查询或文件查询),那么正常的顺序可能是(括号里面代表执行时间):
a、读取文件1 (10ms)
b、处理1的数据(1ms)
c、读取文件2 (10ms)
d、处理2的数据(1ms)
e、读取文件3 (10ms)
f、处理3的数据(1ms)
g、整合1、2、3的数据结果 (1ms)
单线程总共就需要34ms。
那如果你在这个请求内,把ab、cd、ef分别分给3个线程去做,就只需要12ms了。
所以多线程不是没怎么用,而是,你平常要善于发现一些可优化的点。然后评估方案是否应该使用。假设还是上面那个相同的问题:但是每个步骤的执行时间不一样了。
a、读取文件1 (1ms)
b、处理1的数据(1ms)
c、读取文件2 (1ms)
d、处理2的数据(1ms)
e、读取文件3 (28ms)
f、处理3的数据(1ms)
g、整合1、2、3的数据结果 (1ms)单线程总共就需要34ms。
如果还是按上面的划分方案(上面方案和木桶原理一样,耗时取决于最慢的那个线程的执行速度),在这个例子中是第三个线程,执行29ms。那么最后这个请求耗时是30ms。比起不用单线程,就节省了4ms。但是有可能线程调度切换也要花费个1、2ms。
因此,这个方案显得优势就不明显了,还带来程序复杂度提升。不太值得。那么现在优化的点,就不是第一个例子那样的任务分割多线程完成。而是优化文件3的读取速度。可能是采用缓存和减少一些重复读取。首先,假设有一种情况,所有用户都请求这个请求,那其实相当于所有用户都需要读取文件3。
那你想想,100个人进行了这个请求,相当于你花在读取这个文件上的时间就是28×100=2800ms了。那么,如果你把文件缓存起来,那只要第一个用户的请求读取了,第二个用户不需要读取了,从内存取是很快速的,可能1ms都不到。伪代码:
看起来好像还不错,建立一个文件名和文件数据的映射。如果读取一个map中已经存在的数据,那么就不不用读取文件了。可是问题在于,Servlet是并发,上面会导致一个很严重的问题,死循环。因为,HashMap在并发修改的时候,可能是导致循环链表的构成!!!(具体你可以自行阅读HashMap源码)如果你没接触过多线程,可能到时候发现服务器没请求也巨卡,也不知道什么情况!好的,那就用ConcurrentHashMap,正如他的名字一样,他是一个线程安全的HashMap,这样能轻松解决问题。
这样真的解决问题了吗,这样虽然只要有用户访问过文件a,那另一个用户想访问文件a,也会从fileName2Data中拿数据,然后也不会引起死循环。可是,如果你觉得这样就已经完了,那你把多线程也想的太简单了,骚年!你会发现,1000个用户首次访问同一个文件的时候,居然读取了1000次文件(这是最极端的,可能只有几百)。What the fuckin hell!!!难道代码错了吗,难道我就这样过我的一生!好好分析下。Servlet是多线程的,那么
上面注释的“偶然”,这是完全有可能的,因此,这样做还是有问题。因此,可以自己简单的封装一个任务来处理。
以上所有代码都是直接在bbs打出来的,不保证可以直接运行。
多线程最多的场景:web服务器本身;各种专用服务器(如游戏服务器);多线程的常见应用场景:
1、后台任务,例如:定时向大量(100w以上)的用户发送邮件;
2、异步处理,例如:发微博、记录日志等;
3、分布式计算
======================================================================
在java中,每一个线程有一块工作内存区,其中存放着被所有线程共享的主内存中的变量的值的拷贝。当线程执行时,它在自己的工作内存中操作这些变量。
为了存取一个共享的变量,一个线程通常先获取锁定并且清除它的工作内存区,这保证该共享变量从所有线程的共享内存区正确地装入到线程的工作内存区,当线程解锁时保证该工作内存区中变量的值协会到共享内存中。
当一个线程使用某一个变量时,不论程序是否正确地使用线程同步操作,它获取的值一定是由它本身或者其他线程存储到变量中的值。例如,如果两个线程把不同的值或者对象引用存储到同一个共享变量中,那么该变量的值要么是这个线程的,要么是那个线程的,共享变量的值不会是由两个线程的引用值组合而成。
一个变量时Java程序可以存取的一个地址,它不仅包括基本类型变量、引用类型变量,而且还包括数组类型变量。保存在主内存区的变量可以被所有线程共享,但是一个线程存取另一个线程的参数或者局部变量时不可能的,所以开发人员不必担心局部变量的线程安全问题。
volatile变量–多线程间可见
由于每个线程都有自己的工作内存区,因此当一个线程改变自己的工作内存中的数据时,对其他线程来说,可能是不可见的。为此,可以使用volatile关键字破事所有线程军读写内存中的变量,从而使得volatile变量在多线程间可见。
声明为volatile的变量可以做到如下保证:
1、其他线程对变量的修改,可以及时反应在当前线程中;2、确保当前线程对volatile变量的修改,能及时写回到共享内存中,并被其他线程所见;3、使用volatile声明的变量,编译器会保证其有序性。
同步关键字synchronized
同步关键字synchronized是Java语言中最为常用的同步方法之一。在JDK早期版本中,synchronized的性能并不是太好,值适合于锁竞争不是特别激烈的场合。在JDK6中,synchronized和非公平锁的差距已经缩小。更为重要的是,synchronized更为简洁明了,代码可读性和维护性比较好。
锁定一个对象的方法:
当method()方法被调用时,调用线程首先必须获得当前对象所,若当前对象锁被其他线程持有,这调用线程会等待,犯法结束后,对象锁会被释放,以上方法等价于下面的写法:
其次,使用synchronized还可以构造同步块,与同步方法相比,同步块可以更为精确控制同步代码范围。一个小的同步代码非常有离与锁的快进快出,从而使系统拥有更高的吞吐量。
synchronized也可以用于static函数:
这个地方一定要注意,synchronized的锁是加在当前Class对象上,因此,所有对该方法的调用,都必须获得Class对象的锁。
虽然synchronized可以保证对象或者代码段的线程安全,但是仅使用synchronized还是不足以控制拥有复杂逻辑的线程交互。为了实现多线程间的交互,还需要使用Object对象的wait()和notify()方法。
典型用法:
在使用wait()方法前,需要获得对象锁。在wait()方法执行时,当前线程或释放obj的独占锁,供其他线程使用。
当等待在obj上线程收到obj.notify()时,它就能重新获得obj的独占锁,并继续运行。注意了,notify()方法是随机唤起等待在当前对象的某一个线程。
下面是一个阻塞队列的实现:
synchronized配合wait()、notify()应该是Java开发者必须掌握的基本技能。
Reentrantlock重入锁
Reentrantlock称为重入锁。它比synchronized拥有更加强大的功能,它可以中断、可定时。在高并发的情况下,它比synchronized有明显的性能优势。
Reentrantlock提供了公平和非公平两种锁。公平锁是对锁的获取是先进先出,而非公平锁是可以插队的。当然从性能上分析,非公平锁的性能要好得多。因此,在无特殊需要,应该优选非公平锁,但是synchronized提供锁业不是绝对公平的。Reentrantlock在构造的时候可以指定锁是否公平。
在使用重入锁时,一定要在程序最后释放锁。一般释放锁的代码要写在finally里。否则,如果程序出现异常,Loack就永远无法释放了。synchronized的锁是JVM最后自动释放的。
经典使用方式如下:
Reentrantlock提供了非常丰富的锁控制功能,灵活应用这些控制方法,可以提高应用程序的性能。不过这里并非是极力推荐使用Reentrantlock。重入锁算是JDK中提供的高级开发工具。
ReadWriteLock读写锁
IntelliJ IDEA 的缓存和索引主要是用来加快文件查询,从而加快各种查找、代码提示等操作的速度。但是,IntelliJ IDEA 的索引和缓存并不是一直会良好地支持 IntelliJ IDEA 的,这某些特殊条件下,IntelliJ IDEA 的缓存和索引文件也是会损坏的,比如:断电、蓝屏引起的强制关机,当你重新打开 IntelliJ IDEA,基本上百分八十的可能 IntelliJ IDEA 都会报各种莫名其妙错误,甚至项目打不开,IntelliJ IDEA 主题还原成默认状态。也有一些即使没有断电、蓝屏,也会有莫名奇怪的问题的时候,也很有可能是 IntelliJ IDEA 缓存和索引出问题,这种情况还不少
清除缓存和索引
IntelliJ IDEA 已经自带提供清除缓存、索引的路口
一般建议点击 Invalidate and Restart,这样会比较干净。但是有一个需要提醒的是,如上图红圈标注的地方:清除索引和缓存会使得 IntelliJ IDEA 的 Local History 丢失,所以如果你项目没有加入到版本控制,而你又需要你项目文件的历史更改记录,那你最好备份下你的 LocalHistory 目录。目录地址在:C:\Users\当前登录的系统用户名\.IntelliJIdea14\system\LocalHistory 建议使用硬盘的全文搜索,这样效率更高
通过上面方式清除缓存、索引本质也就是去删除 C 盘下的 system 目录下的对应的文件而已,所以如果你不用上述方法也可以删除整个 system。当 IntelliJ IDEA 再次启动项目的时候会重新创建新的 system 目录以及对应项目缓存和索引
如果你遇到了因为索引、缓存坏了以至于项目打不开,那也建议你可以直接删除 system 目录,一般这样都可以很好地解决你的问题
1.点击菜单的File,Invalidate-caches按钮
2.弹出个警告,说本地历史记录也会被清除。 本地历史记录清除的话,例如你的文件想回退...
3.我们选择清空并重启,这次启动需要重新建立索引,会花很长时间。
4.我们找到缓存所在的硬盘目录,发现里面原来几百兆的东西,只剩下不到1兆了。
5.打开上次打开的文件会出现一直Loading的现象,这很正常,是在重新建立索引。
6.缓存文件很快增大到几十兆了,随着你的日常使用,很快又会增大到几百兆。
7.也可以在启动idea前直接把这个chache文件夹删除,启动后也会正常重新建立索引。
1、点击菜单的File,Invalidate-caches按钮
2、弹出个警告,说本地历史记录也会被清除。
本地历史记录清除的话,例如你的文件想回退到之前本地保存的某个编辑版本,就无法回退了。
invalidate的意思是使之失效、作废的意思。就是把以前的缓存清空。
restart是重启idea软件的意思。
这里你可以选择清空、重启、清空并重启。
只清空不重启的话,不会生效,得到下次启动idea前才会清空缓存并重新建立。
3、我们选择清空并重启,这次启动需要重新建立索引,会花很长时间。
4、我们找到缓存所在的硬盘目录,发现里面原来几百兆的东西,只剩下不到1兆了。
5、打开上次打开的文件会出现一直Loading的现象,这很正常,是在重新建立索引。
6、缓存文件很快增大到几十兆了,随着你的日常使用,很快又会增大到几百兆。
7、也可以在启动idea前直接把这个chache文件夹删除,启动后也会正常重新建立索引。
从Eclipse转IDEA的小伙伴对于IDEA的高大上所吸引,毕竟是IDE,很多操作都有区别,一时不习惯在所难免,对于初学者经常需要使用重启或者清理缓存来解决一些自己也不知道的问题,抱着可能就试一试。奈何IDEA的清理缓存并不在run下,而是在File下。如图所示,好事多磨,熟能生巧。
第一步:File–>Invalidate Cache/Restart…
第二步:选择自己需要的操作即可。
1.点击菜单的File,Invalidate-caches按钮
2.弹出个警告,说本地历史记录也会被清除。 本地历史记录清除的话,例如你的文件想回退
到之前本地保存的某个编辑版本,就无法回退了。
invalidate的意思是使之失效、作废的意思。就是把以前的缓存清空。
restart是重启idea软件的意思。
这里你可以选择清空、重启、清空并重启。
只清空不重启的话,不会生效,得到下次启动idea前才会清空缓存并重新建立。
3.我们选择清空并重启,这次启动需要重新建立索引,会花很长时间。
4.我们找到缓存所在的硬盘目录,发现里面原来几百兆的东西,只剩下不到1兆了。
5.打开上次打开的文件会出现一直Loading的现象,这很正常,是在重新建立索引
6.缓存文件很快增大到几十兆了,随着你的日常使用,很快又会增大到几百兆。
7.也可以在启动idea前直接把这个chache文件夹删除,启动后也会正常重新建立索引。
1、点击菜单的File,Invalidate-caches按钮
2、
弹出个警告,说本地历史记录也会被清除。
本地历史记录清除的话,例如你的文件想回退到之前本地保存的某个编辑版本,就无法回退了。
invalidate的意思是使之失效、作废的意思。就是把以前的缓存清空。
restart是重启idea软件的意思。
这里你可以选择清空、重启、清空并重启。
只清空不重启的话,不会生效,得到下次启动idea前才会清空缓存并重新建立。
3、我们选择清空并重启,这次启动需要重新建立索引,会花很长时间。
4、我们找到缓存所在的硬盘目录,发现里面原来几百兆的东西,只剩下不到1兆了。
5、打开上次打开的文件会出现一直Loading的现象,这很正常,是在重新建立索引。
6、缓存文件很快增大到几十兆了,随着你的日常使用,很快又会增大到几百兆。
7、也可以在启动idea前直接把这个chache文件夹删除,启动后也会正常重新建立索引。
IntelliJ IDEA超快的搜索速度和强大的代码提示就是依靠缓存实现的。
缓存文件很大,建立缓存也很花时间,在建立缓存时如果遇到断电或者强退等问题会导致以后打开文件出错等问题,解决办法就是把以前的缓存清理掉。
下面介绍如何清理缓存。
方法/步骤
点击菜单的File,Invalidate-caches按钮
弹出个警告,说本地历史记录也会被清除。
本地历史记录清除的话,例如你的文件想回退到之前本地保存的某个编辑版本,就无法回退了。
invalidate的意思是使之失效、作废的意思。就是把以前的缓存清空。
restart是重启idea软件的意思。
这里你可以选择清空、重启、清空并重启。
只清空不重启的话,不会生效,得到下次启动idea前才会清空缓存并重新建立。
我们选择清空并重启,这次启动需要重新建立索引,会花很长时间。
我们找到缓存所在的硬盘目录,发现里面原来几百兆的东西,只剩下不到1兆了。
打开上次打开的文件会出现一直Loading的现象,这很正常,是在重新建立索引。
缓存文件很快增大到几十兆了,随着你的日常使用,很快又会增大到几百兆。
也可以在启动idea前直接把这个chache文件夹删除,启动后也会正常重新建立索引。
相关问题推荐
Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回false。我们可以通过它的getResultSet方法来获取ResultSet,或者通过getUpda...
忙的时候项目期肯定要加班 但是每天加班应该还不至于
虽然Java人才越来越多,但是人才缺口也是很大的,我国对JAVA工程师的需求是所有软件工程师当中需求大的,达到全部需求量的60%-70%,所以Java市场在短时间内不可能饱和。其次,Java市场不断变化,人才需求也会不断增加。马云说过,未来的制造业要的不是石油,...
工信部证书含金量较高。工信部是国务院的下属结构,具有发放资质、证书的资格。其所发放的证书具有较强的权威性,在全国范围内收到认可,含金量通常都比较高。 工信部证书,其含义也就是工信部颁发并承认的某项技能证书,是具有法律效力的,并且是国家认可的...
学Java好不好找工作?看学完Java后能做些什么吧。一、大数据技术Hadoop以及其他大数据处理技术都是用Java或者其他,例如Apache的基于Java 的 HBase和Accumulo以及ElasticSearchas。但是Java在此领域并未占太大空间,但只要Hadoop和ElasticSearchas能够成长壮...
就是java的基础知识啊,比如Java 集合框架;Java 多线程;线程的五种状态;Java 虚拟机;MySQL (InnoDB);Spring 相关;计算机网络;MQ 消息队列诸如此类
#{}和${}这两个语法是为了动态传递参数而存在的,是Mybatis实现动态SQL的基础,总体上他们的作用是一致的(为了动态传参),但是在编译过程、是否自动加单引号、安全性、使用场景等方面有很多不同,下面详细比较两者间的区别:1.#{} 是 占位符 :动态解析 ...
没问题的,专科学历也能学习Java开发的,主要看自己感不感兴趣,只要认真学,市面上的培训机构不少都是零基础课程,能跟得上,或是自己先找些资料学习一下。
1、反射对单例模式的破坏采用反射的方式另辟蹊径实例了该类,导致程序中会存在不止一个实例。解决方案其思想就是采用一个全局变量,来标记是否已经实例化过了,如果已经实例化过了,第 二次实例化的时候,抛出异常2、clone()对单例模式的破坏当需要实现单例的...
优点: 一、实例控制 单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。 二、灵活性 因为类控制了实例化过程,所以类可以灵活更改实例化过程。 缺点: 一、开销 虽然数量很少,但如果每次对象请求引用时都要...
这个主要是看你数组的长度是多少, 比如之前写过的一个程序有个数组存的是各个客户端的ip地址:string clientIp[4]={XXX, xxx, xxx, xxx};这个时候如果想把hash值对应到上面四个地址的话,就应该对4取余,这个时候p就应该为4...
哈希表的大小 · 关键字的分布情况 · 记录的查找频率 1.直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b,其中a和b为常数(这种散列函数叫做自身函数)。...
哈希表的大小取决于一组质数,原因是在hash函数中,你要用这些质数来做模运算(%)。而分析发现,如果不是用质数来做模运算的话,很多生活中的数据分布,会集中在某些点上。所以这里最后采用了质数做模的除数。 因为用质数做了模的除数,自然存储空间的大小也用质数了...
是啊,哈希函数的设计至关重要,好的哈希函数会尽可能地保证计算简单和散列地址分布均匀,但是,我们需要清楚的是,数组是一块连续的固定长度的内存空间
解码查表优化算法,seo优化
1.对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。2.哈希值就是这个元素的位置。3.如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就...