1、Linux查看日志的三种命令:
第一种:查看实时变化的日志(比较吃内存)
最常用的:
tail -f filename (默认最后10行,相当于增加参数 -n 10)
Ctrl+c 是退出tail命令
其他情况:
tail -n 20 filename (显示filename最后20行)
tail -n +5 filename (从第5行开始显示文件)
第二种:搜索关键字附近的日志
最常用的:
cat -n filename |grep "关键字"
其他情况:
cat filename | grep -C 5 '关键字' (显示日志里匹配字串那行以及前后5行)
cat filename | grep -B 5 '关键字' (显示匹配字串及前5行)
cat filename | grep -A 5 '关键字' (显示匹配字串及后5行)
第三种:进入编辑查找:vi(vim)
1、进入vim编辑模式:vim filename
2、输入“/关键字”,按enter键查找
3、查找下一个,按“n”即可
退出:按ESC键后,接着再输入:号时,vi会在屏幕的最下方等待我们输入命令
wq! 保存退出;
q! 不保存退出;
其他情况:
/关键字 注:正向查找,按n键把光标移动到下一个符合条件的地方
?关键字 注:反向查找,按shift+n 键,把光标移动到下一个符合条件的
2、Linux查看进程命令
ps命令
-a,查看所有
-u,以用户(user)的格式显示
-x, 显示后台进程运行参数
-ef,以全格式显示进程所有信息,包括父进程Pid,创建人,创建时间,进程号。等等
一般项目中,我们首先要查询一个进程,并对其进行删除会用一下命令
ps -a | grep helloworld 或
ps -ef |grep helloworld 或者其他
查询到helloworld相关的进程,我们通过kill命令来操作该进程号删除该进程,kill -9 13492
3、HTTP、HTTPS等常用的默认端口号
1,HTTP服务器,默认端口号为80/tcp(木马Executor开放此端口)
2,HTTPS(securely transferring web pages)服务器,默认端口号为443/tcp 443/udp
3,HTTP协议代理服务器常用端口号:80/8080/3128/8081/9098
4,SOCKS代理协议服务器常用端口号:1080
5,FTP(文件传输)协议代理服务器常用端口号:21
6,Telnet(远程登录)协议代理服务器常用端口号:23
7,Telnet(不安全的文本传送),默认端口号为23/tcp(木马Tiny Telnet Server所开放的端口)
8,FTP,默认的端口号为21/tcp(木马Doly Trojan、Fore、Invisible FTP、WebEx、WinCrash和Blade Runner所开放的端口)
9,TFTP(Trivial File Transfer Protocol),默认端口号为69/udp
10,SSH(安全登录)、SCP(文件传输)、端口号重定向,默认的端口号为22/tcp
11、SMTP Simple Mail Transfer Protocol(E-mail),默认端口号为25/tcp(木马Antigen、Email Password Sender、Haebu Coceda、Shtrilitz Stealth、WinPC、WinSpy都开放这个端口)
12、POP3 Post Office Protocol(E-mail),默认端口号为110/tcp
13、Webshpere应用程序,默认端口号为9080
14、webshpere管理工具,默认端口号9090
15、JBOSS,默认端口号为8080
16、TOMCAT,默认端口号为8080
17、WIN2003远程登录,默认端口号为3389
18、Symantec AV/Filter for MSE,默认端口号为 8081
19、Oracle 数据库,默认的端口号为1521
20、ORACLE EMCTL,默认的端口号为1158
21、Oracle XDB(XML 数据库),默认的端口号为8080
22、Oracle XDB FTP服务,默认的端口号为2100
23、MS SQL SERVER数据库server,默认的端口号为1433/tcp 1433/udp
24、MS SQL SERVER数据库monitor,默认的端口号为1434/tcp 1434/udp
4、HTTP与HTTPS的区别?
http协议和https协议的区别:传输信息安全性不同、连接方式不同、端口不同、证书申请方式不同
一、传输信息安全性不同
1、http协议:是超文本传输协议,信息是明文传输。如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息。
2、https协议:是具有安全性的ssl加密传输协议,为浏览器和服务器之间的通信加密,确保数据传输的安全。
二、连接方式不同
1、http协议:http的连接很简单,是无状态的。
2、https协议:是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议。
三、端口不同
1、http协议:使用的端口是80。
2、https协议:使用的端口是443.
四、证书申请方式不同
1、http协议:免费申请。
2、https协议:需要到ca申请证书,一般免费证书很少,需要交费。
https://blog.csdn.net/weixin_36219512/article/details/79939318
5、数据库的内连接和外连接有什么区别?
内连接:指连接结果仅包含符合连接条件的行,参与连接的两个表都应该符合连接条件。
外连接:连接结果不仅包含符合连接条件的行同时也包含自身不符合条件的行。包括左外连接、右外连接和全外连接。
1、内连接
内连接,即最常见的等值连接,例:
SELECT * FROM TESTA,TESTBWHERE TESTA.A=TESTB.A
结果:
2、外连接
外连接分为左外连接,右外连接和全外连接。
左外连接 left outer join 或者 left join
左外连接就是在等值连接的基础上加上主表中的未匹配数据,例:
SELECT *FROM TESTA LEFT OUTER JOIN TESTB ON TESTA.A=TESTB.A
结果:
扩展资料:
全外连接 full outer join 或者 full join
全外连接是在等值连接的基础上将左表和右表的未匹配数据都加上。
SELECT * FROM TESTA FULL OUTER JOIN TESTBON TESTA.A=TESTB.A
结果:
6、数据库的去重?
1)、distinct
#最简单的去重
select distinct * from table(表名) where (条件)
# 过滤整个表不重复的记录
select distinct * from 表名
#对指定列去重复,多个列使用逗号分开即可
select distinct 字段1 from 表名;
select distinct 字段1, 字段2 from 表名;
#返回表中Col1列不重复的记录行数
select count(distinct 字段) from 表名;
2)、group by 函数
7、JAVA内存管理
java是跨平台语言,java预编译.class文件放置JVM虚拟机中运行;
Java的内存结构,也就是运行时的数据区域
方法区:常量池、变量等存储地方;(持久区)
堆:实例对象存储地方;GC重点关照位置;(新生代和老年代)
程序计数器:记录程序下一步指令;
Java方法栈:方法程序运行地方;Java栈总是与线程关联在一起的,每当创建一个线程,JVM就会为该线程创建对应的Java栈;
本地方法栈:java方法与本地相关联
Java内存模型(即Java Memory Model,简称JMM)本身是一种抽象的概念,并不真实存在;每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),用于存储线程私有的数据,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量;JMM与Java内存区域的划分是不同的概念层次,Java内存模型是围绕着并发编程中原子性、可见性、有序性这三个特征来建立的;
JVM运行机制:JVM转入环境和配置(java.exe-->jvm.cfg)、装载JVM(通过LoadJavaVM来装载)、启动JVM获得本地调用接口、运行java程序;
1.类加载器:
包括启动类加载器(核心库)(jre/lib内所有class)、扩展类(扩展库)(jre/lib/ext/内所有class)、应用程序类(加载classpath下的所有class)
流程(五个阶段):加载、连接(验证、准备、解析)、初始化
2.执行引擎:执行classes中指令。任何JVM specification实现(JDK)的核心都是执行引擎。
3.运行时数据区又叫JVM内存;
内存区域包括堆和栈,栈后进先出,有大小限制,堆可以存放很大内容;java虚拟机中的堆主要存放对象实例,栈存放引用和基本类型;
如:Object o=new Object(); Object存放在堆中,o存放在栈中;
程序计数器:标记程序运行到的位置,JVM相当于一台抽象计算机;
JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完成,通过下面4步来完成JVM环境.
1.创建JVM装载环境和配置
2.装载JVM.dll
3.初始化JVM.dll并挂界到JNIENV(JNI调用接口)实例
4.调用JNIEnv实例装载并处理class类。
二、垃圾回收(GC,全称:garbage colletion)
JAVA虚拟机的GC有自动的垃圾回收机制,不需要开发人员主动调用;由于是自动,优先级又低,所以并不能保证一定执行垃圾回收;
(1)什么时候回收?
当内存不足或者当前空闲的时候进行垃圾回收,GC线程优先级都不太高;
(2)判断什么是垃圾?
一般来说,对于没有引用指向的对象,被标识为垃圾,没有对象指向它,也就无法对它进行操作,这个对象对于我们来说就是没用的;
(3)垃圾回收方法
a.finalize方法工作原理:一旦垃圾回收器准备好释放对象存储空间时,会调用一次并且仅调一次finalize方法(通过调用System.gc()实现),并且当下一次垃圾回收开始时才真正回收对象占用内存;所以重现finalize方法即可在回收时重新建立引用关联,而达到不被回收效果;
b.标记(mark):标记出哪些不是垃圾,回收的时候把没有标记到的认为是垃圾,进行回收;
c.引用计数法:针对每个对象实例的引用进行计算,将计算为0的作为垃圾进行回收,弊端,循环引用将无法进行回收;
d.年轻代:新生成对象放在年轻代中,收集生命周期短的;老年代:被多次垃圾回收的对象;持久代:用于存放静态文件,如java类或方法等;
(1)对新生代的对象的收集称为minor GC;
(2)对旧生代的对象的收集称为Full GC;
(3)程序中主动调用System.gc()强制执行的GC为Full GC。
不同的对象引用类型, GC****会采用不同的方法进行回收,JVM****对象的引用分为了四种类型:
(1)强引用:默认情况下,对象采用的均为强引用(这个对象的实例没有其他对象引用,GC时才会被回收)
(2)软引用:软引用是Java中提供的一种比较适合于缓存场景的应用(只有在内存不够用的情况下才会被GC)
(3)弱引用:在GC时一定会被GC回收
(4)虚引用:由于虚引用只是用来得知对象是否被GC
三、内存调优
主要针对年轻代、年老代、持久代;堆栈等大小进行设置;
内存溢出分2类:
1. 年老代溢出,表现为:java.lang.OutOfMemoryError:Javaheapspace
2. 持久代溢出,表现为:java.lang.OutOfMemoryError:PermGenspace
参数说明
- -Xmx3550m:设置JVM最大堆内存为3550M。
- -Xms3550m:设置JVM初始堆内存为3550M。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
- -Xss128k:设置每个线程的栈大小。JDK5.0以后每个线程栈大小为1M,之前每个线程栈大小为256K。应当根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。需要注意的是:当这个值被设置的较大(例如>2MB)时将会在很大程度上降低系统的性能。
- -Xmn2g:设置年轻代大小为2G。在整个堆内存大小确定的情况下,增大年轻代将会减小年老代,反之亦然。此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8。
- -XX:NewSize=1024m:设置年轻代初始值为1024M。
- -XX:MaxNewSize=1024m:设置年轻代最大值为1024M。
- -XX:PermSize=256m:设置持久代初始值为256M。
- -XX:MaxPermSize=256m:设置持久代最大值为256M。
- -XX:NewRatio=4:设置年轻代(包括1个Eden和2个Survivor区)与年老代的比值。表示年轻代比年老代为1:4。
- -XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的比值。表示2个Survivor区(JVM堆内存年轻代中默认有2个大小相等的Survivor区)与1个Eden区的比值为2:4,即1个Survivor区占整个年轻代大小的1/6。
- -XX:MaxTenuringThreshold=7:表示一个对象如果在Survivor区(救助空间)移动了7次还没有被垃圾回收就进入年老代。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代,对于需要大量常驻内存的应用,这样做可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代存活时间,增加对象在年轻代被垃圾回收的概率,减少Full GC的频率,这样做可以在某种程度上提高服务稳定性。
四、内存溢出常见的哪几种?
三种:
1、新生代和老年代溢出:java.lang.OutOfMemoryError:java heep space;当98%时间用于垃圾回收时,且可用的Heap size 不足2%的时候将抛出此异常信息;
解决方法:手动设置JVM Heap(堆)的大小
2、持久代溢出:java.lang.OutOfMemoryError: PermGen space
解决方法: 通过-XX:PermSize和-XX:MaxPermSize设置永久代大小即可。
3、栈溢出:java.lang.StackOverFlowError:Thread stack space
栈区远远小于堆区,栈区需要的内存大小1-2m左右;出现栈溢出,即说明单线程运行程序需要的内存太大;
解决方法:1:修改程序。2:通过 -Xss: 来设置每个线程的Stack大小即可。
五、java自带分析工具:
jstack(查看线程)、jmap(查看内存)和jstat(性能分析)命令
问题汇总:
1.什么是java虚拟机?为什么java平台被称为“平台无关的编程语言”?
答:java虚拟机是一个可以执行java字节码的虚拟机进程;java虚拟机支持任意平台上运行,java程序编写好后,只要编译成java字节码(class文件)就可以在java虚拟机上运行,java程序无须根据不同环境进行编译;
2.java代码是怎样运行的?
答:java虚拟机运行时数据区分五个区域:方法区、堆、java方法栈、本地方法栈和pc寄存器;java程序被编译成java字节码后,首先要存放在方法区;方能在java虚拟机中运行;为了提高运行效率,标准JDK中的HotSpot虚拟机采用的是一种混合执行的策略。
3.java虚拟机是如何加载java类的?
答:java虚拟机将字节流转换java类三个步骤:装载、链接(验证、准备、解析)、初始化;
4.jvm的永久代会进行垃圾回收吗?
答:jvm的永久代(方法区)、常量池和变量的存储区,垃圾回收不会发生在永久代,当永久代存储满了或超过了临界值后才会触发完全垃圾回收(full gc);jdk8后永久代已经去除,改存元空间区(本地内存区)中;最大可利用空间就变成了整个系统内存的可用空间.
5.对象什么时候可以被垃圾回收?
答:当对象不再被任何对象引用时,可以被回收
6.System.gc()和Rumtime.gc()会做什么事情?
答:都是提示JVM要进行垃圾回收,但是具体什么时候进行垃圾回收要看JVM虚拟机;
7.堆的形状是一颗()。
完全二叉树
满二叉树
二叉排序树
平衡二叉树
答案:A
8、内存溢出怎么处理?
https://blog.csdn.net/xxxx3/article/details/81009524
几种常见的内存溢出与解决办法
内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
代码中存在死循环或循环产生过多重复的对象实体;
使用的第三方软件中的BUG;
启动参数内存值设定的过小;
堆:设置JVM值得方法是通过-XMS(堆的最小值),-XMX (堆的最大值)
栈:设置栈的的大小方法是-XSS参数
PermGen space:第三个异常是关于perm的异常内容,我们需要的是设置方法区的大小,
实现方式是通过设置-XX:PermSize和-XX:MaxPermSize参数
DirectMemory:第四个异常估计遇到的人就不多了,是DirectMemory内存相关的
DirectMemoruSize可以通过设置 -XX:MaxDirectMemorySize参数指定容量大小,如果不指定的话,那么就跟堆的最大值一致
内存使用监控工具jvmstat
【情况一】:
java.lang.OutOfMemoryError:Javaheapspace:这种是java堆内存不够,一个原因是真不够(如递归的层数太多等),
另一个原因是程序中有死循环;如果是java堆内存不够的话,可以通过调整JVM下面的配置来解决:
-Xms3062m
-Xmx3062m
【情况二】
java.lang.OutOfMemoryError:GCoverheadlimitexceeded
【解释】:JDK6新增错误类型,当GC为释放很小空间占用大量时间时抛出;一般是因为堆太小,导致异常的原因,没有足够的内存。
【解决方案】:
1、查看系统是否有使用大内存的代码或死循环;
2、通过添加JVM配置,来限制使用内存:
-XX:-UseGCOverheadLimit
【情况三】:
java.lang.OutOfMemoryError:PermGenspace:这种是P区内存不够,可通过调整JVM的配置:
-XX:MaxPermSize=128m
-XXermSize=128m
【注】:
JVM的Perm区主要用于存放Class和Meta信息的,Class在被Loader时就会被放到PermGenspace,
这个区域成为年老代,GC在主程序运行期间不会对年老区进行清理,默认是64M大小,
当程序需要加载的对象比较多时,超过64M就会报这部分内存溢出了,需要加大内存分配,一般128m足够。
【情况四】:
java.lang.OutOfMemoryError:Directbuffermemory
调整-XX:MaxDirectMemorySize=参数,如添加JVM配置:
-XX:MaxDirectMemorySize=128m
【情况五】:
java.lang.OutOfMemoryError:unabletocreatenewnativethread
【原因】:Stack空间不足以创建额外的线程,要么是创建的线程过多,要么是Stack空间确实小了。
【解决】:由于JVM没有提供参数设置总的stack空间大小,但可以设置单个线程栈的大小;
而系统的用户空间一共是3G,除了Text/Data/BSS/MemoryMapping几个段之 外,Heap和Stack空间的总量有限,
是此消彼长的。因此遇到这个错误, 可以通过两个途径解决:
1.通过-Xss启动参数减少单个线程栈大小,这样便能开更多线程(当然不能太小,太小会出现StackOverflowError);
2.通过-Xms-Xmx两参数减少Heap大小,将内存让给Stack(前提是保证Heap空间够用)。
【情况六】:
java.lang.StackOverflowError
【原因】:这也内存溢出错误的一种,即线程栈的溢出,要么是方法调用层次过多(比如存在无限递归调用),要么是线程栈太小。
【解决】:优化程序设计,减少方法调用层次;调整-Xss参数增加线程栈大小
9、SQL语句返回结果集?
10、mysql的sql语句的优化策略?
特别注意:索引跟出参也有相关联系
索引注意:是否能使用索引,取决于mysql查询优化器对统计数据分析后,是否认为使用索引更快。因此,单纯的讨论一条sql是否可以使用索引有点片面,还需要考虑数据。
一个表最多可有16个索引。最大索引长度是256个字节,尽管这可以在编译MySQL时被改变;理想状态约 5个左右。
SQL语句优化策略:
1、表的主键、外键必须有索引;
2、sql有个最左原则,字段离散率越高索引效率越好的原则(离散率低的都没有必要建索引)
3、经常与其他表进行连接的表,在连接字段上应该建立索引;
4、经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;
5、索引应该建在选择性高的字段上;
6、索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;
7、复合索引的建立需要进行仔细分析;尽量考虑用单字段索引代替:
现在MySQL已经支持同时走多个单独的索引,如果使用联合索引,那么where条件也要尽量根据联合索引的顺序来
A、正确选择复合索引中的主列字段,一般是选择性较好的字段;
B、复合索引的几个字段是否经常同时以AND方式出现在Where子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引;
C、如果复合索引中包含的字段经常单独出现在Where子句中,则分解为多个单字段索引;
D、如果复合索引所包含的字段超过3个,那么仔细考虑其必要性,考虑减少复合的字段;
E、如果既有单字段索引,又有这几个字段上的复合索引,一般可以删除复合索引;
8、频繁进行数据操作的列(修改频繁),不要建立太多的索引;
9、删除无用的索引,避免对执行计划造成负面影响;
10、不要对数据库的字段进行函数处理 如:where comments= #{comments} 前面一个comments属于数据库字段 后面**#{comments} 是可以用函数处理的,但是前面的comments ** 数据库的字段是不能被函数处理的,否则不会走索引。
11、 in索引在主键的时候也会走索引
12、sql语句在大于或者大于等于的时候会走索引,但是在小于或者小于等于的时候没有走索引
个人经验:
个人经验:内连接时外键设为索引时 则 主表索引级别 **eq_ref **若在外键的表建立索引此索引会失效
所以 使用强制的主键索引 FORCE INDEX (PRI)
联表时子句适当先用子句过滤数据也会提升几十倍的速度
个人经验:左(右)连接 左表不走索引,但是如果在GROUP BY pm.id(左表主键)
若不是主键强制索引 **FORCE INDEX (PRI) ** 则左表的索引为 index 类型
若在where后跟上主键字段则两张表的索引类型都为const
配置的优化策略
1、为了解决上面(1)的问题,采用延迟索引。"延迟关联" :通过使用覆盖索引查询返回需要的主键,再根据主键关联原表获得需要的数据。
通过延迟关联解决(1)无法使用覆盖索引的问题,使其能够使用覆盖索引。
explain select * from article join (select comments from
article where category_id=12) as t on t.comments=article.comments;
2、优化UNION查询。
MySQL通过创建并填充临时表的方式来执行UNION查询,
因此需要手工的将where、limit、order by
等子句“下推”到UNION
的各个子查询中,除非确实需要服务器消除重复的行,否则一定要使用UNION ALL
,如果没有ALL
关键字,MySQL会给临时表加上distinct
,从而对临时表的数据做唯一性检查,这样代价非常高。
3、优化关联查询,确保on
或者using
子句中的列上有索引。
确保group by
和order by
的表达式只涉及一个表中的列,
这样MySQL才有可能使用索引来优化整个过程。
4、优化group by和distinct。
MySQL使用同样的方法优化这两类查询,通常是利用索引的顺序性进行优化。但是如果无法使用索引,group by
使用两种策略来完成:使用临时表或者文件排序来做分组。
5、只有索引列顺序和order by
子句的顺序完全一致,并且所有列的排序方向都一致时,MySQL才能使用索引来对结果做排序
6、使用索引覆盖扫描
把所有需要的列都放入索引,这样存储引擎无须回表获取对应行就可以返回结果;
7、优化MySQL的参数
通过优化MySQL的参数可以提高资源利用率,从而达到提高MySQL服务器性能的目的。如下是一些该方面参数的介绍。该参数的配置信息都在my.cnf
或者my.ini
文件中。
key_buffer_size:表示索引缓冲区的大小。所有线程共享缓冲区。增加索引缓冲区可以得到更好处理的索引。当然如果这个值太大,会导致操作系统频换换页,降低系统性能。
table_cache:表示同时打开表的个数,该值越大能够同时打开的表的个数越多。如果打开表的个数太多会影响操作系统的性能。
query_cache_size:查询缓冲区的大小。该值和query_cache_type
配合使用。当query_cache_type=0
,所有查询都不使用缓冲区,但是MySQL并不会释放query_cache_size
所配置的缓冲区内存。当query_cache_type=1
,所有查询使用缓冲区,除非在查询语句中指定SQL_NO_CACHE
,如SELECT SQL_NO_CACHE * FROM table_name;
当query_cache_type=2
,只有在查询语句中使用SQL_CACHE
关键字,查询才会使用缓冲区。使用查询缓冲区可以提高查询速度,这种方式适用于修改操作少且经常执行相同的查询操作的情况。
innodb_buffer_pool_size:表示InnoDB
类型的表和索引的最大缓存,该值越大查询速度就会越快。
max_connections:表示数据库的最大连接数。该值过大会浪费内存资源,严重可能会导致MySQL服务器僵死。
sort_buffer_size:表示每个需要排序线程分配的缓冲区的大小。该值越大排序的速度越快。增加该值可以提高ORDER BY
或GROUP BY
操作的速度。默认值为2M。
8、使用索引合并的案例
https://blog.csdn.net/caomiao2006/article/details/52144964
explain select * from test where (key1_part1=4 and key1_part2=4) or key2_part1=4\G
只有and
和or
同时出现时才会走复合索引 但是具体要看mysql分析器
9、Innodb行锁优化建议
尽可能让所有的数据检索都通过索引来完成,从而避免Innodb
因为无法通过索引键加锁而升级为表级锁定;
合理设计索引,让Innodb
在索引键上面加锁尽可能准确,尽可能的缩小锁定范围,避免造成不必要的锁定而影响其他Query
的执行;
尽可能减少基于范围的数据检索过滤条件,避免间隙锁带来的负面影响而锁定了不该锁定的记录;
尽量控制事务的大小,减少锁定的资源量和锁定时间长度;
在业务环境允许的情况下,尽量使用较低级别的事务隔离,以减少MySQL因为实现事务隔离级别所带来的附加成本;
11、多线程创建方法
方式一:继承Thread类,重写run方法
优势:编写简单
劣势:单继承的限制——无法继承其他父类,同时不能实现资源共享
方式二:实现Runnable接口,并实现run方法
优势:可以继承其他类,多线程可以共享一个Thread对象;
劣势:编程方式稍微复杂,如需访问当前线程,需要调用Thread.currentThread()方法。
12、Java开发多线程是如何解决安全问题的?
https://baijiahao.baidu.com/s?id=1622235965740475862&wfr=spider&for=pc
1、多线程编程的三个核心概念
原子性这一点,类比数据库事务的原子性;即一个操作,也有可能是一组操作,要么全部生效,要么全部失效。 关于原子性,一个非常经典的案例就是银行卡之间转账的问题:比如A和B同时向C转账10万元。如果转账操作不具有性,A在向C转账时,读取了C的余额为20万,然后加上转账的10万,计算出此时应该有30万,但还未来及将30万写回C的账户,此时B的转账请求过来了,B发现C的余额为20万,然后将其加10万并写回。然后A的转账操作继续——将30万写回C的余额。这种情况下C的最终余额为30万,而非预期的40万。
可见性当多个线程并发访问共享变量时,一个线程对共享变量的修改,其它线程能够立即看到。可见性问题是好多人忽略或者理解错误的一点。 CPU从主内存中读数据的效率相对来说不高,现在主流的计算机中,都有几级缓存。每个线程读取共享变量时,都会将该变量加载进其对应CPU的高速缓存里,修改该变量后,CPU会立即更新该缓存,但并不一定会立即将其写回主内存(实际上写回主内存的时间不可预期)。此时其它线程(尤其是不在同一个CPU上执行的线程)访问该变量时,从主内存中读到的就是旧的数据,而非第一个线程更新后的数据。这一点是操作系统或者说是硬件层面的机制
顺序性指的是,程序执行的顺序按照代码的先后顺序执行。如下面这段代码
boolean started = false;
long counter = 0L;
ounter = 1;
started = true;
从代码顺序上看,上面这4条语句应该一次执行,但实际上JAVA 虚拟机真正执行这段代码时,并不保证他们一定按顺序执行。处理器为了提高程序整体的执行效率,可能会对代码进行优化,其中一项优化方式就是调整代码顺序,按照更高效的顺序执行代码。讲到这里,我曾经问过自己,CPU不按照我的代码顺序执行,那怎么保证得到我们想要的结果?实际上,CPU 虽然不保证完全按照代码顺序执行,但他会保证程序的最终执行结果和代码顺序执行结果一致。
2.Java如何解决多线程并发问题
2.1Java如何保证原子性
常用的保证Java操作原子性的工具是锁和同步方法(或者同步代码块)。使用锁,可以保证同一时间只有一个线程能拿到锁,也就保证了同一时间只有一个线程能执行申请锁和释放锁之间的代码。
public void testLock () {
lock.lock();
try{
int j = i;
i = j + 1;
} finally {
lock.unlock();
}
}
与锁类似的是同步方法或者同步代码块。使用非静态同步方法时,锁住的是当前实例;使用静态同步方法时,锁住的是该类的Class对象;使用静态代码块时,锁住的是synchronized关键字后面括号内的对象。下面是同步代码块示例
public void testLock () {
synchronized (anyObject){
int j = i;
i = j + 1;
}
}
小节:无论使用锁还是synchronized,本质都是一样,通过锁来实现资源的排它性,从而实际目标代码段同一时间只会被一个线程执行,进而保证了目标代码段的原子性。这是一种以牺牲性能为代价的方法。
2.2CAS(compare and swap)
基础类型变量自增(i++)是一种常被新手误以为是原子操作而实际不是的操作。Java中提供了对应的原子操作类来实现该操作,并保证原子性,其本质是利用了CPU级别的CAS指令。由于是CPU级别的指令,其开销比需要操作系统参与的锁的开销小。AtomicInteger使用方法如下。
AtomicInteger atomicInteger = new AtomicInteger();
for(int b = 0; b < numThreads; b++) {
new Thread(() -> {
for(int a = 0; a < iteration; a++) {
atomicInteger.incrementAndGet();
}
}).start();
}
2.3 Java如何保证可见性
Java提供了volatile关键字来保证可见性。当使用volatile修饰某个变量时,它会保证对该变量的修改会立即被更新到内存中,并且将其它缓存中对该变量的缓存设置成无效,因此其它线程需要读取该值时必须从主内存中读取,从而得到最新的值。
2.4Java如何保证顺序性
上文讲过编译器和处理器对指令进行重新排序时,会保证重新排序后的执行结果和代码顺序执行的结果一致,所以重新排序过程并不会影响单线程程序的执行,却可能影响多线程程序并发执行的正确性。 Java中可通过volatile在一定程序上保证顺序性,另外还可以通过synchronized和锁来保证顺序性。 synchronized和锁保证顺序性的原理和保证原子性一样,都是通过保证同一时间只会有一个线程执行目标代码段来实现的。 除了从应用层面保证目标代码段执行的顺序性外,JVM还通过被称为happens-before原则隐式地保证顺序性。两个操作的执行顺序只要可以通过happens-before推导出来,则JVM会保证其顺序性,反之JVM对其顺序性不作任何保证,可对其进行任意必要的重新排序以获取高效率。
2.5 happens-before原则(先行发生原则)
传递规则:如果操作1在操作2前面,而操作2在操作3前面,则操作1肯定会在操作3前发生。该规则说明了happens-before原则具有传递性锁定规则:一个unlock操作肯定会在后面对同一个锁的lock操作前发生。这个很好理解,锁只有被释放了才会被再次获取volatile变量规则:对一个被volatile修饰的写操作先发生于后面对该变量的读操作程序次序规则:一个线程内,按照代码顺序执行线程启动规则:Thread对象的start()方法先发生于此线程的其它动作线程终结原则:线程的终止检测后发生于线程中其它的所有操作线程中断规则: 对线程interrupt()方法的调用先发生于对该中断异常的获取对象终结规则:一个对象构造先于它的finalize发生
3.volatile适用场景
volatile适用于不需要保证原子性,但却需要保证可见性的场景。一种典型的使用场景是用它修饰用于停止线程的状态标记。如下所示
boolean isRunning = false;
public void start () {
new Thread( () -> {
while(isRunning) {
someOperation();
}
}).start();
}
public void stop () {
isRunning = false;
}
在这种实现方式下,即使其它线程通过调用stop()方法将isRunning设置为false,循环也不一定会立即结束。可以通过volatile关键字,保证while循环及时得到isRunning最新的状态从而及时停止循环,结束线程。
4.本人面试中被问到的多线程安全问题总结
下面的问题是我在重庆和成都面试的时候被问到的问题,当时不懂的已经下来查资料搞定了,仅供参考
Q:平时项目中使用锁和synchronized比较多,而很少使用volatile,难道就没有保证可见性?
A:锁和synchronized即可以保证原子性,也可以保证可见性。都是通过保证同一时间只有一个线程执行目标代码段来实现的。
Q:锁和synchronized为何能保证可见性?
A:根据JDK中对concurrent包的说明,一个线程的写结果保证对另外线程的读操作可见,只要该写操作可以由happen-before原则推断出在读操作之前发生。
Q:既然锁和synchronized即可保证原子性也可保证可见性,为何还需要volatile?A:synchronized和锁需要通过操作系统来仲裁谁获得锁,开销比较高,而volatile开销小很多。因此在只需要保证可见性的条件下,使用volatile的性能要比使用锁和synchronized高得多。
Q:既然锁和synchronized可以保证原子性,为什么还需要AtomicInteger这种的类来保证原子操作?
A:锁和synchronized需要通过操作系统来仲裁谁获得锁,开销比较高,而AtomicInteger是通过CPU级的CAS操作来保证原子性,开销比较小。所以使用AtomicInteger的目的还是为了提高性能。
Q:还有没有别的办法保证线程安全?
A:有。尽可能避免引起非线程安全的条件——共享变量。如果能从设计上避免共享变量的使用,即可避免非线程安全的发生,也就无须通过锁或者synchronized以及volatile解决原子性、可见性和顺序性的问题。
Q:synchronized即可修饰非静态方式,也可修饰静态方法,还可修饰代码块,有何区别?
A:synchronized修饰非静态同步方法时,锁住的是当前实例;synchronized修饰静态同步方法时,锁住的是该类的Class对象; synchronized修饰静态代码块时,锁住的是synchronized关键字后面括号内的对象。
12、线程池创建
https://www.cnblogs.com/jxxblogs/p/11655670.html
13、JVM的垃圾回收机制 总结(垃圾收集、回收算法、垃圾回收器)
https://www.cnblogs.com/aspirant/p/8662690.html
一、哪些内存需要回收?
JVM的内存结构包括五大区域:程序计数器、虚拟机栈、本地方法栈、堆区、方法区。其中程序计数器、虚拟机栈、本地方法栈3个区域随线程而生、随线程而灭,因此这几个区域的内存分配和回收都具备确定性,就不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。而Java堆区和方法区则不一样,这部分内存的分配和回收是动态的,正是垃圾收集器所需关注的部分。
垃圾收集器在对堆区和方法区进行回收前,首先要确定这些区域的对象哪些可以被回收,哪些暂时还不能回收,这就要用到判断对象是否存活的算法!
14、jvm内存模型-和内存分配以及jdk、jre、jvm是什么关系
https://www.cnblogs.com/aspirant/p/6841955.html
15、ArrayList和LinkedList的区别?
https://www.cnblogs.com/lingshang/p/10897912.html
16、HashMap 底层实现原理、put方法的实现原理
https://blog.csdn.net/suifeng629/article/details/82179996
https://www.cnblogs.com/stm32stm32/p/9035519.html
17、java中为什么说静态方法先执行?
https://zhidao.baidu.com/question/558556926.html
18、#与$的区别?
介绍
MyBatis中使用parameterType向SQL语句传参,parameterType后的类型可以是基本类型int,String,HashMap和java自定义类型。
在SQL中引用这些参数的时候,可以使用两种方式#{parameterName}或者${parameterName}。
#{}
#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。
例如:order by #{parameterName} //或取Map中的value#{Key}也是一样操作。
假设传入参数是“Smith”
会解析成:order by "Smith"
${}
$将传入的数据直接显示生成在sql中。
例如:order by #{parameterName} //或取Map中的value#{Key}也是一样操作。
假设传入参数是“Smith”
会解析成:order by Smith
概念
#方式能够很大程度防止sql注入,方式一般用于传入数据库对象,例如传入表名。
从安全性上考虑,能使用#尽量使用#来传参,因为这样可以有效防止SQL注入的问题。
重点
MyBatis排序时使用order by 动态参数时需要注意,用{columnName} //这里MyBatis不会修改或转义字符串,可实现动态传入排序。
建议:接受从用户输出的内容并提供给语句中不变的字符串,这样做是不安全的。
这会导致潜在的SQL注入攻击,因此你不应该允许用户输入这些字段,或者通常自行转义并检查。