云服务器上mail发送超时

问题

没事做了个定时任务,定时给自己发邮件提示某些接口获取到的信息,结果本地调试正常,发到服务器上发现都是发送邮件超时

1
发送邮件报错:Could not connect to SMTP host: smtp.qq.com, port: 25

解决方案

修改发送端口,阿里 和腾讯都把25 端口关闭了.发送需要使用其他端口. 465

1
2
3
4
5
6
7
properties.setProperty("mail.smtp.auth", "true");//开启认证
properties.setProperty("mail.debug", "true");//启用调试
properties.setProperty("mail.smtp.timeout", "1000");//设置链接超时
properties.setProperty("mail.smtp.port", "465");//设置端口
properties.setProperty("mail.smtp.socketFactory.port", "465");//设置ssl端口
properties.setProperty("mail.smtp.socketFactory.fallback", "false");
properties.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mail:
host: smtp.qq.com
username: xxx.com
password: xxx
properties:
mail:
smtp:
post: 465
socketFactory:
port: 465
fallback: false
class: javax.net.ssl.SSLSocketFactory
auth: true
starttls:
enable: true
required: true

Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986

原因分析:
翻译过来就是:在请求目标中发现无效字符。有效字符在RFC 7230和RFC 3986中定义。

百度看看:原来这是从7.0开始的高版本tomcat中的新特性:就是严格按照 RFC 3986规范进行访问解析,而 RFC 3986规范定义了Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有保留字符(RFC3986中指定了以下字符为保留字符:! * ’ ( ) ; : @ & = + $ , / ? # [ ])。

解决方案:
既然知道原因了,那我们应该怎么解决这个问题呢?

网上的几种办法:

1、更换低版本的Tomcat。这个办法表面上是可行的,但是这不是属于自欺欺人嘛。因为在实际的项目开发中,你改动Tomcat很可能也要改动JDK版本和其他jar包版本,这会影响到整个项目的正常开发,没理由大家都跟着你改吧。所以pass掉了。

2、在Tomcat的安装目录中,打开conf/catalina.properties,找到末尾的一行。增加两行配置:

tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}
org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true

还是不行, 换toncat 这个 8.5 的有问题 ,8.0的就没问题了

java反射机制

反射

什么是反射?

反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Apple {

private int price;

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}

public static void main(String[] args) throws Exception{
//正常的调用
Apple apple = new Apple();
apple.setPrice(5);
System.out.println("Apple Price:" + apple.getPrice());
//使用反射调用
//获取类的 Class 对象实例
Class clz = Class.forName("com.chenshuyi.api.Apple");
//根据 Class 对象实例获取 Constructor 对象
Constructor appleConstructor = clz.getConstructor();
//使用 Constructor 对象的 newInstance 方法获取反射类对象
Object appleObj = appleConstructor.newInstance();
//获取方法的 Method 对象
Method setPriceMethod = clz.getMethod("setPrice", int.class);
//利用 invoke 方法调用方法
setPriceMethod.invoke(appleObj, 14);
Method getPriceMethod = clz.getMethod("getPrice");
System.out.println("Apple Price:" + getPriceMethod.invoke(appleObj));
}
}

源码分析:

1
2
3
4
java.lang.NullPointerException 
...
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)

可以看到异常堆栈指出了异常在 Method 的第 497 的 invoke 方法中,其实这里指的 invoke 方法就是我们反射调用方法中的 invoke。

Spring Bean 中的反射
1
2
<bean class="com.chenshuyi.Apple">
</bean>

当我们在 XML 文件中配置了上面这段配置之后,Spring 便会在启动的时候利用反射去加载对应的 Apple 类。而当 Apple 类不存在或发生启发异常时,异常堆栈便会将异常指向调用的 invoke 方法。

从这里可以看出,我们平常很多框架都使用了反射,而反射中最最终的就是 Method 类的 invoke 方法了。

那么Method 的 invoke 方法做了什么呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}

开始是进行了一些权限的检查,最后是调用了 MethodAccessor 类的 invoke 方法进行进一步处理

反射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1 Java反射机制主要功能:

    1.在运行时判断任意一个对象所属的类。

    2.在运行时构造任意一个类的对象。

    3.在运行时判断任意一个类所具有的成员变量和方法。

    4.在运行时调用任意一个对象的方法。

2 对反射提供支持的类(反射功能主要由Class对象和java.lang.relflect包中的类提供支持)

    Class类:代表一个类,位于java.lang包下。

    Field类:代表类的成员变量(成员变量也称为类的属性)。

    Method类:代表类的方法。

    Constructor类:代表类的构造方法。

    Array类:提供了动态创建数组,以及访问数组的元素的静态方法。


####创建对象的两种方法

Class 对象的 newInstance()
使用 Class 对象的 newInstance()方法来创建该 Class 对象对应类的实例,但是这种方法要求该 Class 对象对应的类有默认的空构造器。
调用 Constructor 对象的 newInstance()
先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance()方法来创建 Class 对象对应类的实例,通过这种方法可以选定构造方法创建实例。

1
2
3
4
5
6
7
8
//获取 Person 类的 Class 对象
Class clazz=Class.forName("reflection.Person");
//使用.newInstane 方法创建对象
Person p=(Person) clazz.newInstance();
//获取构造方法并创建对象
Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class);
//创建对象并设置属性
Person p1=(Person) c.newInstance("李四","男",20);

java相关大杂烩

##基础

###类

抽象类与普通类的唯一区别就是不能创建实例对象和允许有abstract方法。

一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就可以认为是抽象类

抽象类可以有构造方法,接口中不能有构造方法。(构造方法,日常初始化数据的那个)

native作用:通知虚拟机,我要调用外部代码了(一般是c或者c++)

###异常

try {}里有一个return语句,什么时候执行finally{}?在执行return后,他继续执行finally,return的结果是没有执行finally前的。

运行时异常与一般异常有何异同?异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。

Javaweb

Servlet的生命周期包括 加载 、 实例化 、初始化、处理请求、服务结束。这个生存期由javax.servlet.Servlet接口的init(),service()和destroy方法表达。

request.getAttribute()和 request.getParameter()

getParameter()取得是通过容器的实现来取得通过类似post,get等方式传入的数据;

setAttribute()和getAttribute()只是在web容器内部流转,仅仅是请求处理阶段;只有先用setAttribute()设置之后,才能够通过getAttribute()来获得值,它们传递的是Object类型的数据

getAttribute是返回对象,getParameter返回字符串

**jsp有哪些内置对象?作用分别是什么?**JSP共有以下9个内置的对象:

1
2
3
4
5
6
7
8
9
request 用户端请求,此请求会包含来自GET/POST请求的参数
response 网页传回用户端的回应
pageContext 网页的属性是在这里管理
session 与请求有关的会话期
application servlet 正在执行的内容
out 用来传送回应的输出
config servlet的构架部件
page JSP网页本身
exception 针对错误网页,未捕捉的例外

JDBC访问数据库的基本步骤 加载驱动,通过DriverManager获取连接对象,通过连接对象获取会话,通过会话进行数据的增删改查,封装对象,关闭连接

JDBC编程中处理事务

1
2
3
conn.setAutoComit(false);设置提交方式为手工提交
conn.commit()提交事务
出现异常,回滚 conn.rollback();

数据库连接池的原理。为什么要使用连接池。

创建连接费时,所以统一创建,多个操作共享同一连接。使用连接池可以提高对数据库连接资源的管理。基本思想是为数据库连接建立“缓冲池”,创建几个连接在缓冲区,调用时获取缓冲池,用完再放回去。

脏读,幻读问题 脏读:数据为读取到了还没持久化,可能进行回滚的数据。幻读:表示在使用事务过程中,多次查询一条数据得到的结果不同。解决方案都是使用事务隔离级别配置。

JDBC的DriverManager用来做什么JDBC的DriverManager是一个工厂类,用来创建连接,当Driver类被加载,会自动注册到工厂中。

execute,executeQuery,executeUpdate的区别

Statement的execute(String query)方法用来执行任意的SQL查询,如果查询结果是一个ResultSet,这个方法就返回true。如果不是ResultSet,比如insert或者update查询,它就会返回false。

Statement的executeQuery(String query)接口用来执行select查询,并且返回ResultSet。即使查询不到记录返回的ResultSet也不会为null。我们通常使用executeQuery来执行查询语句。如果传insert或者update会抛异常。

Statement的executeUpdate(String query)方法用来执行insert或者update/delete(DML)语句,或者 什么也不返回,对于DDL语句,返回值是int类型,如果是DML语句的话,它就是更新的条数,如果是DDL的话,就返回0。

只有当你不确定是什么语句的时候才应该使用execute()方法

JDBC的ResultSet是什么ResultSet对象维护了一个游标,指向当前的数据行。开始的时候这个游标指向的是第一行。如果调用了ResultSet的next()方法游标会下移一行,如果没有更多的数据了,next()方法会返回false。可以在for循环中用它来遍历数据集。

Get与Post 按HTTP规范来讲,Get用于信息获取,应该是幂等的,Post用于数据修改提交等。但是多数开发情况下,Get也用作提交了,写起来方便。Get最多提交1014个字节

###泛型

Java中的泛型是什么 ? 使用泛型的好处是什么参数化类型,也就是说所操作的数据类型被指定为一个参数。1、类型安全,提供编译期间的类型检测 2、前后兼容 3、泛化代码,代码可以更多的重复利用 4、性能较高,用GJ(泛型JAVA)编写的代码可以为java编译器和虚拟机带来更多的类型信息,这些信息对java程序做进一步优化提供条件。

框架

Struts其实就是个Servlet,已经多年没人用了。

Hibernate 对象关系映射,坑爹的缓存

使用Hibernate的基本流程是:配置Configuration对象、产生SessionFactory、创建session对象,启动事务,完成CRUD操作,提交事务,关闭session。

行级锁

开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

表级锁

开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。

页级锁

页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁.表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。BDB支持页级锁

MyBatis中使用#和$书写占位符有什么区别?
#将传入的数据都当成一个字符串,会对传入的数据自动加上引号;

​ $将传入的数据直接显示生成在SQL中。

​ 注意:使用$占位符可能会导致SQL注射攻击,能用#的地方就不要使用$,写order by子句的时候应该用​$而不是#。

MyBatis中的动态SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MyBatis中用于实现动态SQL的元素主要有: 
- if - choose / when / otherwise - trim - where - set - foreach
<select id="foo" parameterType="Blog" resultType="Blog">
select * from t_blog where 1 = 1
<if test="title != null">
and title = #{title}
</if>
<if test="content != null">
and content = #{content}
</if>
<if test="owner != null">
and owner = #{owner}
</if>
</select>

JDBC编程有哪些不足之处,mybatis如何优化**

JDBC连接频繁创建,mybatis在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接

JDBC代码混写,mybatis在XXXXmapper.xml文件中与java代码分离

JDBC解析麻烦,mybatis自动映射java对象

MyBatis的一级缓存和二级缓存?

Mybatis首先去缓存中查询结果集,如果没有则查询数据库,如果有则从缓存取出返回结果集就不走数据库。Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象
Mybatis的二级缓存即查询缓存,它的作用域是一个mapper的namespace,即在同一个namespace中查询sql可以从缓存中获取数据。二级缓存是可以跨SqlSession的。

iBatis与Hibernate

相同点:屏蔽jdbc api的底层访问细节,使用我们不用与jdbc api打交道,就可以访问数据。

ibatis的好处:屏蔽jdbc api的底层访问细节;将sql语句与java代码进行分离;提供了将结果集自动封装称为实体对象和对象的集合的功能,queryForList返回对象集合,用queryForObject返回单个对象;提供了自动将实体对象的属性传递给sql语句的参数。

Hibernate是一个全自动的orm映射工具,它可以自动生成sql语句,ibatis需要我们自己在xml配置文件中写sql语句,hibernate要比ibatis功能负责和强大很多。但是对于复杂sql,batis更好写。

Hibernate的二级缓存

缓存的作用是可以快速查询,减少数据库压力。Hibernate的Session就是一种缓存,通常将之称为Hibernate的一级缓存,使用session从数据库中查询出一个对象时,Session也是先从自己内部查看是否存在这个对象,存在则直接返回,不存在才去访问数据库,并将查询的结果保存在自己内部。Hibernate的二级缓存就是要为Hibernate配置一种全局缓存,让多个线程和多个事务都可以共享这个缓存。我们希望的是一个人使用过,其他人也可以使用,session没有这种效果。

###其他

单例模式

单例的类别:懒汉、饿汉、枚举、静态内部类、双重校验锁 等等 , 选择线程安全我选最后一种,双重校验锁。

int强转byte会如何?可以强转,但是int是32位的,byte是8位的,导致,强转后,int的高24位会丢失只会保留后低8位

a.hashCode() 有什么用?与 a.equals(b) 有什么关系? hashCode() 方法对应对象整型的 hash 值。两个使用 equal() 方法来判断相等的对象,必须具有相同的 hash code。

i18n 国际化,多套语言模板。

生产者——消费者 问题

日常使用队列存就好了,然后获取,如果队列满了。就找个地方存呗,当队列处理完后,获取存储内容即可。

哲学家进餐

谈谈你对NIO的理解

答:IO是面向流,NIO是面向缓冲 ,这里不细讲了,具体参照:Java NIO和IO的区别

io是面向流的,nio面向的是缓冲区

io是直接读取一个或多个字符,知道全部读完,没有缓存,不能前后移动流中的数据.阻塞的

nio可以前后移动了,非阻塞,

来源地址:

https://www.cnblogs.com/peke/p/7894685.html

https://www.cnblogs.com/lyldaisy/p/10952180.html

JVM常识了解一下

JVM

运行时数据区域包括哪些?

程序计数器(线程私有):当前线程所执行字节码的行号指示器

Java 虚拟机栈(线程私有): 为虚拟机执行 Java 方法(字节码)服务, 虚拟机栈描述的是 Java 方法执行的内存模型:每个方法被执行的时候都会创建一个栈帧,存储局部变量表、操作栈、动态链接、方法出口。 每一个方法被调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

**本地方法栈(线程私有)**:(Native Method Stacks)为虚拟机使用到的 Native 方法服务。

java 堆(线程共享):虚拟机启动时创建(内存最大),用于存放对象实例。垃圾收集器主要管理的就是 Java 堆。Java 堆在物理上可以不连续,只要逻辑上连续即可。

方法区(线程共享): 存储已被虚拟机加载的类信息、常量、静态
变量、即时编译器编译后的代码
等数据,也不需要连续的内存

运行时常量池: 方法去的一部分,保存Class文件的符号引用、翻译出来的直接引用。运行时常量池可以在运行期间将新的常量放入池中。用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

如何判断对象死亡?

引用计数法: 给对象添加一个引用计数器,每当有一个地方引用它,计数器就+1,;当引用失效时,计数器就-1;任何时刻计数器都为0的对象就是不能再被使用的。缺点在于:很难解决对象之间的循环引用问题。

根搜索算法:通过 “GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连(用图论的话来说就是从 GC Roots 到这个对象不可达)时,则证明此对象是不可用的。

java 的4种引用方式

强引用 Strong Reference: 把一个对象赋给一个引用变量,这个引用变量就是一个强引用。Object obj = new Object();这个就是强引用,垃圾收集永远不会回收被引用的对象。

软引用Soft Reference还有用,但非必须的对象。在内存溢出前,会被列入回收范围,并进行二次回收。

弱引用 Weak Reference: 只能生存到下一次垃圾收集发生前

虚引用 Phantom Reference:目的是希望在这个对象被收集器回收时,收到一个系统通知

垃圾回收基本回收策略

标记-清除(Mark-Sweep):第一阶段标记所有被引用的对象,第二阶段遍历整个堆,删除未标记对象,需要暂停,会产生内存碎片

复制(Copying)-新生代:划分2块,回收时,将未使用的复制到另一块,空间成本高,在对象存活率较高时,需要执行较多的复制操作,效率会变低。一般用来回收新生代,一般是8:1,1是新生代。

标记-整理(Mark-Compact)-老年代: 同标记,第二阶段把存活对象“压缩”到堆的其中一块,按顺序排放

分代收集算法:把堆分为新生代(用复制算法)老生代(用标记-清理)

Minor GC (新生代) Full GC (老生代),一般老比新慢10倍以上。

**永久代(Permanent Generation)**: 用于存储被 JVM 加载的类信息、 常量、 静态变量、 即时编译器编译后的代码等数据(java8已经移除,被元数据区取代,元空间并不在虚拟机中,而是使用本地内存)

Class文件

组成:Class文件是一组以8位字节为基础单位的二进制流,各个数据项目间没有任何分隔符。当遇到8位字节以上空间的数据项时,则会按照高位在前的方式分隔成若干个8位字节进行存储。

魔数: 每个Class文件的头4个字节称为魔数(Magic Number),它的唯一作用是用于确定这个文件是否为一个能被虚拟机接受的Class文件

版本号:第5,6字节是次版本号,第7,8字节是主版本号。

类加载器

作用:实现加载动作,用于确定类。任意一个类都需要由类加载器这个类本身确立其在虚拟机中的唯一性

什么是双亲委派模型?

双亲委派模型要求除了顶层的启动类加载器外,其余加载器都应当有自己的父类加载器。类加载器之间的父子关系,通过组合关系复用。

工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有到父加载器反馈自己无法完成这个加载请求(它的搜索范围没有找到所需的类)时,子加载器才会尝试自己去加载。

为什么要使用双亲委派模型,组织类加载器之间的关系?

Java类随着它的类加载器一起具备了一种带优先级的层次关系。比如java.lang.Object,它存放在rt.jar中,无论哪个类加载器要加载这个类,最终都是委派给启动类加载器进行加载,因此Object类在程序的各个类加载器环境中,都是同一个类。

如果没有使用双亲委派模型,让各个类加载器自己去加载,那么Java类型体系中最基础的行为也得不到保障,应用程序会变得一片混乱。

什么是volatile?

关键字volatile是Java虚拟机提供的最轻量级的同步机制(线程不安全),性能优于锁。2个特性:

  1. 保证此变量对所有线程的可见性。当一条线程修改了这个变量的值,新值对于其他线程是可以立即得知的。

  2. 禁止指令重排序优化。普通变量仅仅能保证在该方法执行过程中,得到正确结果,但是不保证程序代码的执行顺序

4种锁状态

在JavaSE1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。

锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区,避免了线程切换的开心。(让自己循环取值,等其他人完成后改变值才醒来,占cpu,但恢复快)缺点是占用处理器时间。

偏向锁:消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。即在无竞争的情况下,把整个同步都消除掉。这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他的线程获取,则持有偏向锁的线程将永远不需要同步。

轻量级锁:在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。

公平锁:多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。

Java对象的生命周期

创建阶段 、 应用阶段 、不可见阶段 、不可达阶段 、收集阶段 、终结阶段、 对象空间重新分配阶段 详情

描述一下JVM加载class文件的原理机制?

JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。

什么是类加载机制?

虚拟机的类加载机制是 :虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。

JVM 类加载机制分为五个部分:加载,验证,准备,解析,初始化(后面还有 使用和卸载)。

GC是什么?为什么要有GC?

GC是垃圾回收,java自动检测不在作用域的对象,自动清理内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。GC通过有向图确定对象是否 “可达”,对不可达对象进行回收。可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行

Java 中堆和栈有什么区别?

JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。

​ 栈:在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。

​ 堆:堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。

分布式、多线程、锁

分布式、多线程、锁

是什么 - 为什么 - 怎么用

什么是分布式 为了解决单个物理服务器容量和性能瓶颈问题而采用的优化手段。 水平扩展,垂直拆分。

什么是线程?线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位

线程和进程有什么区别? 线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。每个线程都拥有单独的栈内存用来存储本地数据。

如何在Java中实现线程 继承java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程,Callable和Future创建线程 util包下。

什么是线程池? 为什么要使用它?

​ 创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。

​ 为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。

​ 从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。比如单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线程池)。

join方法 把指定的线程加入到当前线程中

主线程启动子线程,如果子线程中要进行大量的耗时运算,主线程会早于子线程结束,这时候主线程如果想等待子线程完成之后再运行,就需要join()方法

多线程 与 JUC

AMQP模型

JAVA 线程实现/创建方式

继承 Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。start()方法是一个 native 方法,它将启动一个新线程,并执行 run()方法。

1
2
3
4
5
6
7
public class MyThread extends Thread {
public void run() {
System.out.println("MyThread.run()");
}
}
MyThread myThread1 = new MyThread();
myThread1.start();

实现 Runnable 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
}
//启动 MyThread,需要首先实例化一个 Thread,并传入自己的 MyThread 实例:
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
//事实上,当传入一个 Runnable target 参数给 Thread 后, Thread 的 run()方法就会调用
target.run()
public void run() {
if (target != null) {
target.run();
}
}

ExecutorService、 Callable、 Future 有返回值线程

有返回值的任务必须实现 Callable 接口,类似的,无返回值的任务必须 Runnable 接口。执行
Callable 任务后,可以获取一个 Future 的对象,在该对象上调用 get 就可以获取到 Callable 任务
返回的 Object 了,再结合线程池接口 ExecutorService 就可以实现传说中有返回结果的多线程了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
// 创建多个有返回值的任务
List<Future> list = new ArrayList<Future>();
for (int i = 0; i < taskSize; i++) {
Callable c = new MyCallable(i + " ");
// 执行任务并获取 Future 对象
Future f = pool.submit(c);
list.add(f);
}
// 关闭线程池
pool.shutdown();
// 获取所有并发任务的运行结果
for (Future f : list) {
// 从 Future 对象上获取任务的返回值,并输出到控制台
System.out.println("res: " + f.get().toString());
}

juc汇总

  1. Executors:具有Runnable任务的执行者。
  2. ExecutorService:一个线程池管理者,其实现类有多种,我会介绍一部分,我们能把Runnable,Callable提交到池中让其调度。
  3. Semaphore:一个计数信号量。
  4. ReentrantLock:一个可重入的互斥锁定Lock,功能类似synchronized,但要强大的多。
  5. Future:是与Runnable,Callable进行交互的接口,比如一个线程执行结束后取返回的结果等,还提供了cancel终止线程。
  6. BlockingQueue:阻塞队列。
  7. CompletionService:ExecutorService的扩展,可以获得线程执行结果的。
  8. CountDownLatch:一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
  9. CyclicBarrier:一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点。
  10. Future:表示异步计算的结果。
  11. ScheduldExecutorService:一个ExecutorService,可安排在给定的延迟后运行或定期执行的命令。

多线程操作(比如异步通知,或者一些操作的后续处理,为了不影响相应时间),我们是启动的时候new了队列,BlockingQueue LinkedBlockingQueue

1. 线程与进程

进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。

一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

区别
a,地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;
b,资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资
c,线程是处理器调度的基本单位,但进程不是.
d,二者均可并发执行.

2.守护线程

在Java中有两类线程:用户线程 (User Thread)、守护线程 (Daemon Thread)。
守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。

3.java thread状态

  1. NEW 状态是指线程刚创建, 尚未启动
  2. RUNNABLE 状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等
  3. BLOCKED 这个状态下, 是在多个线程有同步操作的场景, 比如正在等待另一个线程的synchronized 块的执行释放, 也就是这里是线程在等待进入临界区
  4. WAITING 这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll 一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在理解点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束
  5. TIMED_WAITING 这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long), join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态
  6. TERMINATED 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)

AMQP模型

4.请说出与线程同步以及线程调度相关的方法

  1. wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
  2. sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
  3. notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
  4. notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

5.进程调度算法

实时系统:FIFO(First Input First Output,先进先出算法),SJF(Shortest Job First,最短作业优先算法),SRTF(Shortest Remaining Time First,最短剩余时间优先算法)。
交互式系统:RR(Round Robin,时间片轮转算法),HPF(Highest Priority First,最高优先级算法),多级队列,最短进程优先,保证调度,彩票调度,公平分享调度。

时间片轮转法

6.wait()和sleep()的区别

  1. sleep来自Thread类,和wait来自Object类
  2. 调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁
  3. sleep睡眠后不出让系统资源,wait让出系统资源其他线程可以占用CPU
  4. sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒

8.Synchronized 与Lock

ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候
线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情

ReentrantLock获取锁定与三种方式:
a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
c)tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

总体的结论先摆出来:

synchronized:
在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronized,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。
ReentrantLock:
ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。

9.线程池

线程池的作用:
在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。
常用线程池:ExecutorService 是主要的实现类,其中常用的有
Executors.newSingleThreadPool(),newFixedThreadPool(),newcachedTheadPool(),newScheduledThreadPool()。

线程池的4个组成部分

线程池管理器:用于创建并管理线程池;

工作线程:线程池中的线程;

任务接口:每个任务必须实现的接口,用于工作线程调度其运行;

任务队列:用于存放待处理的任务,提供一种缓冲机制。

ThreadPoolExecutor
1
2
3
4
5
6
7
8
9
10
11
12
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize, long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
corePoolSize:指定了线程池中的线程数量。
maximumPoolSize:指定了线程池中的最大线程数量。
keepAliveTime:当前线程池数量超过 corePoolSize 时,多余的空闲线程的存活时间,即多
次时间内会被销毁。
unit: keepAliveTime 的单位。
workQueue:任务队列,被提交但尚未被执行的任务。
threadFactory:线程工厂,用于创建线程,一般用默认的即可。
handler:拒绝策略,当任务太多来不及处理,如何拒绝任务。

10.Executor拒绝策略

  1. AbortPolicy:为java线程池默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute需要try
    catch,否则程序会直接退出.
  2. DiscardPolicy:直接抛弃,任务不执行,空方法
  3. DiscardOldestPolicy:从队列里面抛弃head的一个任务,并再次execute 此task。
  4. CallerRunsPolicy:不停重复执行提交,直到插入(好凶残)
  5. 用户自定义拒绝策略:实现RejectedExecutionHandler,并自己定义策略模式

11.4种线程池 - CachedThreadPool 、 FixedThreadPool、SingleThreadPool(固定长度、缺少创建多余删除、延时执行、单例)

  1. newSingleThreadExecutor :创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
    适用场景:任务少 ,并且不需要并发执行
  2. newCachedThreadPool :创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.
    线程没有任务要执行时,便处于空闲状态,处于空闲状态的线程并不会被立即销毁(会被缓存住),只有当空闲时间超出一段时间(默认为60s)后,线程池才会销毁该线程(相当于清除过时的缓存)。新任务到达后,线程池首先会让被缓存住的线程(空闲状态)去执行任务,如果没有可用线程(无空闲线程),便会创建新的线程。
    适用场景:处理任务速度 > 提交任务速度,耗时少的任务(避免无限新增线程)
  3. newFixedThreadPool :创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  4. newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行

12.Java里的阻塞队列

7个阻塞队列

记:数组有界阻塞队列、支持优先级排序无界阻塞、支持优先级队列无界阻塞、不存数据阻塞、链表(有界、无界、双向)阻塞

ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。

按照先进先出(FIFO)原则排序默认情况下不保证访问者公平的访问队列,因为公平访问降低吞吐量。

LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列(两个独立锁提高并发)。

基于链表的阻塞队列,按照先进先出(FIFO)原则排序,其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,能够处理并发数据。

PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列(compareTo 排序实现优先)。

采取自然顺序升序排列,可自定义实现 compareTo()。

DelayQueue:一个使用优先级队列实现的无界阻塞队列。(缓存失效、定时任务)

支持延时获取元素的无界阻塞队列。队列使用 PriorityQueue实现

缓存系统的设计:可以用 DelayQueue 保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从 DelayQueue 中获取元素时,表示缓存有效期到了。

定时任务调度: 使 用 DelayQueue 保 存 当 天 将 会 执 行 的 任 务 和 执 行 时 间 , 一 旦 从DelayQueue 中获取到任务就开始执行,从比如TimerQueue 就是使用 DelayQueue 实现的。

SynchronousQueue:一个不存储元素的阻塞队列。(不存储数据、可用于传递数据)

是一个不存储元素的阻塞队列。每一个 put 操作必须等待一个 take 操作,否则不能继续添加元素。(传递器)

LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。

多了 tryTransfer(试探下有没传入数据,没有就返回false,有没有数据都立即返回结果)、transfer 方法(去取数据,没数据就等,直到有数据才返回)

LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

双向队列指的你可以从队列的两端插入和移出元素。

添加元素

Java中的阻塞队列接口BlockingQueue继承自Queue接口。BlockingQueue接口提供了3个添加元素方法。
add:添加元素到队列里,添加成功返回true,由于容量满了添加失败会抛出IllegalStateException异常
offer:添加元素到队列里,添加成功返回true,添加失败返回false
put:添加元素到队列里,如果容量满了会阻塞直到容量不满

删除方法

3个删除方法
poll:删除队列头部元素,如果队列为空,返回null。否则返回元素。
remove:基于对象找到对应的元素,并删除。删除成功返回true,否则返回false
take:删除队列头部元素,如果队列为空,一直阻塞到队列有元素并删除

13.DelayQueue

队列中每个元素都有个过期时间,并且队列是个优先级队列,当从队列获取元素时候,只有过期元素才会出队列。

14.原子操作类

在java.util.concurrent.atomic包下,可以分为四种类型的原子更新类:原子更新基本类型、原子更新数组类型、原子更新引用和原子更新属性。

原子更新基本类型
使用原子方式更新基本类型,共包括3个类:
AtomicBoolean:原子更新布尔变量
AtomicInteger:原子更新整型变量
AtomicLong:原子更新长整型变量

atomic实现原理:通过CAS(比较并替换 - 乐观锁)的方式实现的,代价是可能循环时间会太长

原子更新数组
通过原子更新数组里的某个元素,共有3个类:
AtomicIntegerArray:原子更新整型数组的某个元素
AtomicLongArray:原子更新长整型数组的某个元素
AtomicReferenceArray:原子更新引用类型数组的某个元素
AtomicIntegerArray常用的方法有:
int addAndSet(int i, int delta):以原子方式将输入值与数组中索引为i的元素相加
boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式更新数组中索引为i的值为update值

原子更新引用类型
AtomicReference:原子更新引用类型
AtomicReferenceFieldUpdater:原子更新引用类型里的字段
AtomicMarkableReference:原子更新带有标记位的引用类型。

原子更新字段类
如果需要原子更新某个类的某个字段,就需要用到原子更新字段类,可以使用以下几个类:
AtomicIntegerFieldUpdater:原子更新整型字段
AtomicLongFieldUpdater:原子更新长整型字段
AtomicStampedReference:原子更新带有版本号的引用类型。
要想原子更新字段,需要两个步骤:
每次必须使用newUpdater创建一个更新器,并且需要设置想要更新的类的字段
更新类的字段(属性)必须为public volatile

15.死锁,以及解决死锁

死锁产生的四个必要条件

互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
循环等待条件:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。

解决死锁

一是死锁预防,就是不让上面的四个条件同时成立。
二是,合理分配资源。
三是使用银行家算法,如果该进程请求的资源操作系统剩余量可以满足,那么就分配。

16.进程间的通信方式

  1. 管道( pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
  2. 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
  3. 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
  4. 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  5. 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
  6. 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
  7. 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

17.怎么解决 hash冲突

1.开放地址法 2.再哈希法 3.链地址法(hashmap) 4.建公共溢出区

juc - 详细名称 concurrent 用过那个,知道为什么这么用 LinkedBlockingQueue (几个子类的区别) 需要记得有哪些子类 - 各有什么区别 。

18.ThreadLocal

hreadLocal为每个线程维护一个本地变量。

采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。
ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本。

4个方法:get/set/remove/ initialValue

可以用于解决SimpleDateFormat的线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class SimpleDateFormatDemo {

private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

private static ThreadLocal threadLocal = new ThreadLocal<>();

/**
* 获取线程的变量副本,如果不覆盖initialValue方法,第一次get将返回null,故需要创建一个DateFormat,放入threadLocal中
* @return
*/
public DateFormat getDateFormat() {
DateFormat df = threadLocal.get();
if (df == null) {
df = new SimpleDateFormat(DATE_FORMAT);
threadLocal.set(df);
}
return df;
}

public static void main(String [] args) {
SimpleDateFormatDemo formatDemo = new SimpleDateFormatDemo();

MyRunnable myRunnable1 = new MyRunnable(formatDemo);
MyRunnable myRunnable2 = new MyRunnable(formatDemo);
MyRunnable myRunnable3 = new MyRunnable(formatDemo);

Thread thread1= new Thread(myRunnable1);
Thread thread2= new Thread(myRunnable2);
Thread thread3= new Thread(myRunnable3);
thread1.start();
thread2.start();
thread3.start();
}


public static class MyRunnable implements Runnable {

private SimpleDateFormatDemo dateFormatDemo;

public MyRunnable(SimpleDateFormatDemo dateFormatDemo) {
this.dateFormatDemo = dateFormatDemo;
}

@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 当前时间:"+dateFormatDemo.getDateFormat().format(new Date()));
}
}
}
源码分析
ThreadLocalMap

ThreadLocalMap内部是利用Entry来进行key-value的存储的。

1
2
3
4
5
6
7
8
static class Entry extends WeakReference<ThreadLocal> {            
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}

上面源码中key就是ThreadLocal,value就是值,Entry继承WeakReference,所以Entry对应key的引用(ThreadLocal实例)是一个弱引用。

set(ThreadLocal key, Object value)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal key, Object value) {
Entry[] tab = table;
int len = tab.length;
//根据ThreadLocal的散列值,查找对应元素在数组中的位置
int i = key.threadLocalHashCode & (len-1);
//采用线性探测法寻找合适位置
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
ThreadLocal k = e.get();
//key存在,直接覆盖
if (k == key) {
e.value = value;
return;
}
// key == null,但是存在值(因为此处的e != null),说明之前的ThreadLocal对象已经被回收了
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//ThreadLocal对应的key实例不存在,new一个
tab[i] = new Entry(key, value);
int sz = ++size;
//清楚陈旧的Entry(key == null的)
// 如果没有清理陈旧的 Entry 并且数组中的元素大于了阈值,则进行 rehash
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}

这个set操作和集合Map解决散列冲突的方法不同,集合Map采用的是链地址法,这里采用的是开放定址法(线性探测)。set()方法中的replaceStaleEntry()和cleanSomeSlots(),这两个方法可以清除掉key ==null的实例,防止内存泄漏。

getEntry()
1
2
3
4
5
6
7
8
private Entry getEntry(ThreadLocal key) {            
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}

由于采用了开放定址法,当前key的散列值和元素在数组中的索引并不是一一对应的,首先取一个猜测数(key的散列值),如果所对应的key是我们要找的元素,那么直接返回,否则调用getEntryAfterMiss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {            
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}

这里一直在探测寻找下一个元素,知道找的元素的key是我们要找的。这里当key==null时,调用expungeStaleEntry有利于GC的回收,用于防止内存泄漏。

ThreadLocal为什么会内存泄漏

ThreadLocalMap的key为ThreadLocal实例,他是一个弱引用,我们知道弱引用有利于GC的回收,当key == null时,GC就会回收这部分空间,但value不一定能被回收,因为他和Current Thread之间还存在一个强引用的关系。由于这个强引用的关系,会导致value无法回收,如果线程对象不消除这个强引用的关系,就可能会出现OOM。有些时候,我们调用ThreadLocalMap的remove()方法进行显式处理。

ThreadLocal总结

ThreadLocal不是用来解决共享变量的问题,也不是协调线程同步,他是为了方便各线程管理自己的状态而引用的一个机制。

每个ThreadLocal内部都有一个ThreadLocalMap,他保存的key是ThreadLocal的实例,他的值是当前线程的局部变量的副本的值。

19.Atomic - Integer

一个提供原子操作的 Integer 的类,同理的还有几个都在atomic包下。在多线程程序中,诸如++i 或 i++等运算不具有原子性,是不安全的线程操作之一。通常我们会使用 synchronized 将该操作变成一个原子操作,但 JVM 为此类操作特意提供了一些同步类,使得使用更方便,且使程序运行效率变得更高。

锁-单机锁如何实现 - sys 的好像就是同步和互斥锁.

同步锁和互斥锁的区别 同步就是一起用,互斥就是谁拿到了就是谁的,其他人睡着.

锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁。 锁可以升级,但不可降级。

偏向锁:目的是在某个线程获得锁之后,消除这个线程锁重入(CAS)的开销,看起来让这个线程得到了偏护

阻塞锁:让线程到阻塞状态等待,得到通知醒来开始竞争.不占cpu资源,但是恢复慢.竞争激烈使用阻塞锁,不激烈使用自旋锁.

乐观锁与悲观锁(为什么? 优势? )

悲观锁 - 假设最差情况- 每次拿别人都在改- 所以每次拿数据都上锁,比如行锁,表锁,读锁,写锁.等 synchronizedReentrantLock等独占锁就是悲观锁思想的实现

乐观锁(CAS)假设都是最好情况,别人不会改,所以不上锁,就判断这期间有没有人改数据,所以通过version判定,适用于多读情况 . 可以提高吞吐量.

乐观锁适用于写比较少的情况下(多读场景)但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

**乐观锁实现 : **1 version. 2 CAS算法 - 比较需要读写的值.与原先相同就修改,不同就不操作.一般情况下是自旋操作,即不断重试.

共享锁与独占锁:共享锁是一种乐观锁 ,独占锁是一种悲观锁

什么是可重入锁(ReentrantLock)?(递归锁)

可重入锁指的是在同一个线程中外层函数获取锁后,内层函数仍然有获取该锁的代码,却不受影响。在java中,ReentrantLock与 synchronized 都是可重入锁。(避免死锁)

https://blog.csdn.net/qq_36299025/article/details/89883989

公平锁与非公平锁

公平锁(Fair):加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得

非公平锁(Nonfair):加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待。

非公平锁性能比公平锁高 5~10 倍,因为公平锁需要在多核的情况下维护一个队列;Java 中的 synchronized 是非公平锁, ReentrantLock 默认的 lock()方法采用的是非公平锁。

分拆锁(lock splitting)与分离锁(lock striping)

如果一个锁守护多个相互独立的状态变量,你可能能够通过拆分锁,使每个锁守护不同的变量,从而改进伸缩性.这样可以使锁的请求频率都变小.分拆锁对于中等竞争强度锁,能够转化为非竞争锁. 分拆锁的扩展,分为若干加锁块集合,并且他们属于相互独立对象,这个情况就是分离锁.如:ConcurrentHashMap就实现了16个锁数组,各自守护1/16. - (原文在深入分布式缓存中)

读写锁

1)多个读者可以同时进行读

2)写者必须互斥(只允许一个写者写,也不能读者、写者同时进行)

3)写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)

Java中读写锁有个接口java.util.concurrent.locks.ReadWriteLock也有具体的实现ReentrantReadWriteLock。

锁优化

**减少锁持有时间 **:只用在有线程安全要求的程序上加锁

减小锁粒度:将大对象拆分为小对象,降低锁竞争,可以提升偏向锁、轻量锁的成功率。典型案例还是ConcurrentHashMap

锁分离:读写锁就是锁分离。 LinkedBlockingQueue 从头部取出,从尾部放数据。

锁粗化:在某些情况下,不停的请求、同步、释放,本身会消耗系统资源,不利于性能优化(与减小粒度相反)。

锁消除:编译器层面优化。详情

使用双锁分离就得注意一点,那就是防止线程夯死。生产线程要唤醒生产线程,消费线程也要唤醒生产线程,消费线程唤醒消费线程,消费线程也要唤醒生产线程。

分布式锁 保证分布式领域中共享数据安全问题

1、数据库实现(效率低,不推荐)

2、redis实现(使用redission实现,但是需要考虑思索,释放问题。繁琐一些)

3、Zookeeper实现 (使用临时节点,效率高,失效时间可以控制)

4、Spring Cloud 实现全局锁(内置的)

https://www.cnblogs.com/wangjintao-0623/p/9727272.html

线程同步方式

synchronized 同步代码块时,一个时间内只能有一个线程得到执行.其他线程需要等待(加法计数器用到 - 计数器+机器码 生成订单号)

同步锁(lock)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Lock是控制多个线程对共享资源进行访问的工具.通常,锁提供了对共享资源的独占访问,每次只能有一个,线程对Lock对象加锁,线程开始访问共享资源之前,应该先获得Lock对象。
 //定义锁对象
private final ReentrantLock lock = new ReentrantLock();
//定义需要保证线程安全的方法
public void m(){
lock.lock();
try{
//需要保证线程安全的代码
//...method body
}
//使用finally块来保证释放锁
finally{
lock.unlock();
}
}

ConcurrentHashMap并发

Lost Wake-Up 问题

多线程情况下,一个消费者线程,一个生产者线程。

生产者

1
2
count+1;
notify();

消费者

1
2
3
while(count<=0)
wait()
count--

如果两个步骤同时执行

lost wake-up

如何解决 - 通过同步的方式:只能有一个执行,就可以解决这个问题。(java有强制要求)

Spring 的一些基础知识

Spring

AOP

面向切面编程,使用”横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与业务逻辑关系不大的部分是横切关注点。横切关注点经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务管理等。

AOP基于代理模式,动态代理 :AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成aop对象.aop对象包含目标的全部方法,并在特定切点进行增强处理,然后回调原对象.

Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理

动态代理

JDK动态代理是基于Java的反射机制实现的,主要涉及到java.lang.reflect包中的Proxy和InvocationHandler。

InvocationHandler是一个接口,通过实现这个接口定义一个横切的逻辑!然后通过反射机制调用目标类的方法,这样就能动态的把非业务逻辑和业务逻辑动态的拼接在一起.

proxy则利用InvocationHandler创建代理实例,来间接的调用代理的方法!

JDK的反射是基于接口的!也就是你的service一定是有接口的不然是不行的!这时候就有个Cglib可以顶上了!

Cglib采用了底层的字节码技术,为代理类创建了一个子类来代理它!

简单实现

1
2
3
4
5
6
7
8
9
10
11
@Service
public class LoginService{
if(isLegal(userId)){
System.out.println(userId + "正在登录");
return;
}
System.out.println(userId + "非法用户,禁止登录");
}
private boolean isLegal(String userId){
return !"user_1".equalsIgnoreCase(userId);
}

插入日志功能

1
2
3
4
5
6
7
8
9
10
11
12
13
@Aspect
@Comonent
public class LoginAspect{
@Arount(value = "execution(* com.universexin.LoginService.login(..))")
private Object loginAspect(ProcedingjoinPoint jp) throw Throwable{
Object[] args = jp.getArgs();
String userId=(String) args[0];
System.out.println("日志:" + userId + "开始登录");
Object result = jp.proceed();
System.out.println("日志:" + userId + "结束登录");
return result;
}
}

知识点

spring优点:低侵入,IOC低耦合,AOP对日志安全等通用应用统一管理,高复用.

IOC是什么,控制反转,让对象创建不用去new.由spring通过反射机制根据配置文件在运行时动态创建对象.spring提供4种集合配置,如:list,set,map,props(双string的map)

事务

Spring 事务传播

required:没有就创建,存在就加入

supports:存在就加入,不存在就不做事务

mandatory:存在就加入,不存在就抛异常

requires_new:不管是否存在都新建

no_supported:如果存在事务将事务挂起.

nerver:存在事务我就抛异常.

nested:存在就嵌套,不存在就创建

事务隔离级别:

default:默认db级别

read_uncommitted:读取未提交(会脏读),基本不用

read_committed:读取已提交(会幻读,不可重复读)

repeatable_read:可重复读,行锁(会幻读)

serializable:完全看不到其他事务的操作,表锁

解释Spring支持的几种bean的作用域。

1
2
3
4
5
6
Spring框架支持以下五种bean的作用域:
singleton : bean在每个Spring ioc 容器中只有一个实例。
prototype:一个bean的定义可以有多个实例。
request:每次http请求都会创建一个bean,该作用域仅在基于web的 Spring ApplicationContext情形下有效。
session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

Spring实现原理

Spring是实现了工厂模式的工厂类。这个类名为BeanFactory(实际上是一个接口),在程序中通常BeanFactory的子类ApplicationContext。Spring相当于一个大的工厂类,在其配置文件中通过元素配置用于创建实例对象的类名和实例对象的属性。

IOC,IOC就是一个容器,负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。很好地实现模块之间的解耦,@Autowired,不用每次去new service。

AOP面向切面,如:系统中有很多各不相干的类的方法,在这些众多方法中要加入某种系统功能的代码(是否能理解是类似于中间件),例如,加入日志,加入权限判断,加入异常处理,Transactions事务,Synchronization同步

实现AOP功能采用的是代理技术,客户端程序不再调用目标,而调用代理类,代理类与目标类对外具有相同的方法声明,有两种方式可以实现相同的方法声明,一是实现相同的接口,二是作为目标的子类。

Spring框架的好处

容器:Spring 包含并管理应用中对象的生命周期和配置。

MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。

事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。

异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。

ApplicationContext通常的实现是什么?

FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。

ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。

WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。

什么是Spring beans

​ Spring beans 是那些形成Spring应用的主干的java对象。它们被Spring IOC容器初始化,装配,和管理。这些beans通过容器中配置的元数据创建。比如,以XML文件中 的形式定义。

​ Spring 框架定义的beans都是单件beans。在bean tag中有个属性”singleton”,如果它被赋为TRUE,bean 就是单件,否则就是一个 prototype bean。默认是TRUE,所以所有在Spring框架中的beans 缺省都是单件。

Spring框架中bean的生命周期

Spring容器从XML 文件中读取bean的定义,并实例化bean

Spring根据bean的定义填充所有的属性

如果bean实现了BeanNameAware 接口,Spring 传递bean 的ID 到 setBeanName方法;

如果Bean 实现了 BeanFactoryAware 接口, Spring传递beanfactory 给setBeanFactory 方法。

如果有任何与bean相关联的BeanPostProcessors,Spring会在postProcesserBeforeInitialization()方法内调用它们。

如果bean实现IntializingBean了,调用它的afterPropertySet方法,如果bean声明了初始化方法,调用此初始化方法。

如果有BeanPostProcessors 和bean 关联,这些bean的postProcessAfterInitialization() 方法将被调用。

如果bean实现了 DisposableBean,它将调用destroy()方法。

SpringMVC的原理

Spring MVC的核心就是 DispatcherServlet , 一个请求经过 DispatcherServlet ,转发给HandlerMapping ,然后经反射,对应 Controller及其里面方法的@RequestMapping地址,最后经ModelAndView和ViewResoler返回给对应视图 。

SpringMVC的原理图10191011

springmvc拦截器的定义和配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//测试拦截器1
public class HandlerInterceptor1 implements HandlerInterceptor{

@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {

System.out.println("HandlerInterceptor1....preHandle");

//false表示拦截,不向下执行;true表示放行
return true;
}

@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {

System.out.println("HandlerInterceptor1....postHandle");

}

@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {

System.out.println("HandlerInterceptor1....afterCompletion");
}
}
  1. preHandle方法:进入Handler方法之前执行。可以用于身份认证、身份授权。比如如果认证没有通过表示用户没有登陆,需要此方法拦截不再往下执行(return false),否则就放行(return true)。
  2. postHandle方法:进入Handler方法之后,返回ModelAndView之前执行。可以看到该方法中有个modelAndView的形参。应用场景:从modelAndView出发:将公用的模型数据(比如菜单导航之类的)在这里传到视图,也可以在这里同一指定视图。
  3. afterCompletion方法:执行Handler完成之后执行。应用场景:统一异常处理,统一日志处理等。

BeanFactory和ApplicationContext有什么区别?

    BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。

(1)BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:

①继承MessageSource,因此支持国际化。

②统一的资源文件访问方式。

③提供在监听器中注册bean的事件。

④同时加载多个配置文件。

⑤载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

①BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
②ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

③相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。

(3)BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。

(4)BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

Autowired与Resource

使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:

如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;

如果查询的结果不止一个,那么@Autowired会根据名称来查找;

如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

@Autowired可用于:构造函数、成员变量、Setter方法

注:@Autowired和@Resource之间的区别

(1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。

(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

来源

https://blog.csdn.net/a745233700/article/details/80959716

spring 事务不回滚

起因

生产遇到一个异常情况,报运行时异常,缺还是修改了数据库数据。

经过

本地测试发现,在保存后,直接抛异常,并没有回滚数据。

用debug查看,发现已经打印了回滚日志了,但是数据库没生效。

由于我们是多数据源情况,感觉是spring的事务是否启动了,

1
<tx:annotation-driven transaction-manager="transactionManagerWallet"/>

xml中加入了,这个,发现,可以。但是,另外的数据源就出问题了。

发现问题

经过测试发现,在类上面加了@Transactional(transactionManager = “transactionManagerWallet”)

然后,方法上又加了@Transactional可能会导致 类上的 事务,被覆盖掉,导致,调到了其他的事务中

解决

后续方法上所有的@Transactional 都需要带对应的transactionManager,或者就不要写,如果类上有的话。这样就不会存在被覆盖的问题。

Lambda 表达式

Lambda 表达式

语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
(parameters) -> expression

(parameters) ->{ statements; }

// 1. 不需要参数,返回值为 5
() -> 5

// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x

// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y

// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y

// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)

//调用方式
public static void main(String args[]){
TService tService = (go) -> test2(go);
String a = tService.sayMessage("xxx");
System.out.println(a);
}

public static String test2(String t){
return t + "test2";
}

public interface TService {
String sayMessage(String go);
}

特征
  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
变量作用域
  • lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
  • lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)
  • 在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

linux常用

linux常用指令

1.0 常用

清除在运行项目日志文件内容

echo “” > catalina.out

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 查询启动
netstat -nltp | grep 8005

# 查询进程
ps -ef|grep java

# 查内存 free -h 人性化显示
total 总内存 used 已使用 free可用

# 下载
wget

# 删除
rm -f -rf

# 解压
unzip(需要安装) tar xf(-zxvf) xx.tar.gz

# 设置权限:
chmod [对象] +/-[权限] [文件] 例如: chmod u+x ./xxx.sh u所有者 加入x执行操作

# 用户
创建 useradd wxyadmin
删除 userdel wxyadmin
密码 passwd wxyadmin

# 防火墙端口
vi /etc/sysconfig/iptables
重启配置 service iptables restart

# 批处理
打印 :echo "请输入1"
设置变量: num = 1
调用变量: ${num}
特定变量:
$n 调第n个参数
$$ 脚本的pid
$# 脚本的参数个数

# 挂载、卸载
挂载: sudo mount /dev/sdb1 /mnt/
卸载 : sudo umount /mnt/

# 当前树形结构
tree lstree

# 查看开放端口
netstat -tnp

# linux 清理日志 定时器:
#!/bin/sh //脚本解析器路径
find /opt/myworld/ -mtime +30 -name "*.log" -exec rm -rf{}\;

crontab -e //将.sh执行脚本加入到系统计划任务
10 0 * * * 路径名+.sh >/dev/null 2>&1
设定每天凌晨的0点10分之子执行.sh

1.1 文件创建

touch 如果不存在文件,则创建文件

1.2 命令描述查询与详情查询

mkdir –help 查看简单命令描述
man mkdir 查看命令详细描述

1.3 文件内容搜索:

cat 文件名 (全部显示)
more 文件名 (分屏显示, 空格后 继续显示)
grep 查询文件是否包含指定内容
使用:grep [-n 对于行号 -v 显示不包含 -i 忽略大小写] 内容 文件名称
同时可以用正则查找 : ^xx xx开头 。 dd$ dd结尾

1.4 重定向

1
2
3
4
> 输出
>> 追加
echo gogogo > a 将gogoo 内容 输出到文件 a 里面 >> 追加 不会覆盖, > 会覆盖原先内容
ls > a 可以将 ls 获取到的当前目录下的内容输出到 a 里面.

1.5 管道 |

1
2
3
将 一个命令的输出 可以通过管道 做为 另一个命令的输入
ls |grep xx 查询 ls 下 包含xx 的内容
ifconfig | grep inet 查 ip信息下的 inet 查看ip地址

1.6 隐藏文件

如果名字前带.说明是 隐藏文件 ,如.sh 如何查看? ls -alh

1.7 SSH 免密登陆

1
2
3
4
5
6
ssh-keygen 生成ssh 密钥 全回车即可
ssh-copy-id root@xx.xx.xx.xx 拷贝公钥

非对等加密算法
本地使用私钥加解密
服务器使用公钥加解密

1.8 配置别名

1
2
3
4
5
6
7
8
在 ~/.ssh/config 中 加入 

Host my
HostName 47.100.217.237
User root
Port 22
my 是别名。
下次登陆只需要 ssh my即可

1.9 scp 文件拷贝

1
scp 文件 root@xx.xx.xx.xx:地址 将文件复制到 地址 中

1.10 权限管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-rw-r--r--  1 unicersexin  staff    56  2 23 09:51 config
-rw------- 1 unicersexin staff 1856 2 23 09:23 id_rsa

universexin 是 拥有者 staff 组
- rw- r-- r--
- rw- --- ---
拥有者权限 组权限 其他用户权限

1 硬链接数 有多少种方式可以访问到该文件

chmod +/-rwx 文件名|目录名 加或者减 表示 添加减少权限。rwx 表示 可读/可写/可执行 可任意组合rwx 文件或目录都可以
如: chmod +rw xx.ssh 或 chmod -x /home/x

组(操作需要 root 权限 macos 好像不用..)
groupadd xx 添加组 xx
groupdel xx 删除组 xx
cat /etc/group 查看组信息

1.11 常用命令

1.11.1 磁盘大小

df(disk free 显示磁盘剩余空间) du -h 目录名 -h 人性化显示

1.11.2 进程信息

ps(process status 查看进程详情) top(动态显示并排序) kill -9(干掉它)

1
ps aux(a显示终端所有进出  u显示进程详细状态   x 显示没有控制终端的进程)

1.11.3 查找文件

不输入路径 表示在当前文件夹下
find [路径] -name “条件 如果 *.py 或者 wxy

1.11.4 软链接(快捷方式) ln -s 被链接的原文件(完整路径) 链接文件名

在linux 中 文件名和文件的数据 是分开存储的.
软连接文件名->软链接数据->文件名->文件数据
软链接文件数据 关联 文件名

硬链接 : ln 不带 -s 会创建硬链接, 文件名与硬链接 都关联文件数据

1.11.5 打包/解包

tar linux 最常用的备份工具.

打包: tar -cvf 打包文件.tar 被打包文件或者路径…(依次添加,接下去)

解包: tar -xvf 捷豹文件.tar

1.11.6 压缩/解压

tar -zcvf 压缩文件.tar.gz
tar -zxcf 解压文件.tar.gz (-C 目标路径。解压到某个路径下)

tar -jcvf 压缩文件.tar.bz2
tar -jxvf 解压文件.tar.bz2

1.11.7 软件安装

sudo apt install 软件包 安装
sudo apt remove 软件名 删
sudo apt upgrade 升级

1.11.8 查询进程 ps -ef|grep java

1.11.9 删除进程 kill -9 进程号

1.11.10 查内存 free

total 总内存 used 已使用 free可用

1.11.11下载 wget

1.11.12 创建

文件夹:mkdir 文件:touch

1.11.13 删除 rm -f -rf

1.11.14 解压

unzip(需要安装) tar xf(-zxvf) xx.tar.gz

1.11.15 设置权限:

chmod [对象] +/-[权限] [文件] 例如: chmod u+x ./xxx.sh u所有者 加入x执行操作

1.11.16 用户

创建 useradd wxyadmin

删除 userdel wxyadmin

密码 passwd wxyadmin

1.11.17 防火墙端口

vi /etc/sysconfig/iptables
重启配置 service iptables restart

1.11.18 批处理

1
2
3
4
5
6
7
打印 :echo "请输入1"
设置变量: num = 1
调用变量: ${num}
特定变量:
$n 调第n个参数
$$ 脚本的pid
$# 脚本的参数个数

1.11.19 挂载、卸载

挂载: sudo mount /dev/sdb1 /mnt/

卸载 : sudo umount /mnt/

1.11.20 现在树形结构 tree lstree

1.11.21 查看开放端口 netstat -tnp

1.11.22 linux 清理日志 定时器:

1
2
3
4
5
6
7
#!/bin/sh    //脚本解析器路径
find /opt/myworld/ -mtime +30 -name "*.log" -exec rm -rf{}\;

crontab -e //将.sh执行脚本加入到系统计划任务
10 0 * * * 路径名+.sh >/dev/null 2>&1
设定每天凌晨的0点10分之子执行.sh

1.11.23 端口处理

查询 netstat -anp 所有开放端口信息

关闭端口号:

1
iptables -A OUTPUT -p tcp --dport 端口号-j DROP

打开端口号:

1
2
iptables -A INPUT -ptcp --dport  8099 -j ACCEPT
service iptables save 保存设置

如果无法使用iptable操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
service iptables save出错please try to use systemctl.

首先停止防火墙
systemctl stop firewalld
systemctl mask firewalld

安装iptables-services
yum install iptables-services

设置开机启动防火墙
systemctl enable iptables

可以使用下面命令管理iptables
systemctl [stop|start|restart] iptables

java 安装

https://blog.csdn.net/zhuimeng_by/article/details/83653940