Spring中的synchronized容易出现不生效的情况，比如：在controller层加事物注解，内部代码块中含synchronized。正确使用方式应当是：同步代码块在事物之前开启为什么先调用@Transactional再调用synchronized会出现问题？此处原文链接：https://blog.csdn.net/Rambo_Yang/article/details/1198855241）事务开启在同步代码块之前2）事务是 Spring 的 AOP 开启的，进入方法前，AOP 就开启了事务3）事务开启以后才进入方法，再进入同步代码块加锁4）当同步方法执行结束后，释放锁并提交事物（问题就出现在这里：如果在释放锁和提交事物之间有其它的线程请求，那么处理后的数据没有被提交，导致 synchronized 同步不生效的问题）
HttpSession 服务端的技术服务器会为每一个用户 创建一个独立的HttpSessionHttpSession原理当用户第一次访问Servlet时,服务器端会给用户创建一个独立的Session并且生成一个SessionID,这个SessionID在响应浏览器的时候会被装进cookie中,从而被保存到浏览器中当用户再一次访问Servlet时,请求中会携带着cookie中的SessionID去访问服务器会根据这个SessionID去查看是否有对应的Session对象有就拿出来使用;没有就创建一个Session(相当于用户第一次访问)域的范围:Context域 > Session域 > Request域 Session域 只要会话不结束就会存在 但是Session有默认的存活时间(30分钟) 需要注意的是，一个Session的概念需要包括特定的客户端，特定的服务器端以及不中断的操作时间。A用户和C服务器建立连接时所处的Session同B用户和C服务器建立连接时所处的Session是两个不同的Session。session进行身份验证的原理： 当客户端第一次访问服务器的时候，此时客户端的请求中不携带任何标识给服务器，所以此时服务器无法找到与之对应的session，所以会新建session对象，当服务器进行响应的时候，服务器会将session标识放到响应头的Set-Cookie中，会以key-value的形式返回给客户端，例：JSESSIONID=7F149950097E7B5B41B390436497CD21；其中JSESSIONID是固定的，而后面的value值对应的则是给该客户端新创建的session的ID，之后浏览器再次进行服务器访问的时候，客户端会将此key-value放到cookie中一并请求服务器，服务器就会根据此ID寻找对应的session对象了；（当浏览器关闭后，会话结束，由于cookie消失所以对应的session对象标识消失，而对应的session依然存在，但已经成为报废数据等待GC回收了）对应session的ID可以利用此方法得到：session.getId();1.client-server connection先上一张图，如下client与server建立一个连接，这种连接是底层的 client发送request到server，等待server的answer server处理request，将处理结果返还给client，这个结果包括status code、其它data 在HTTP/1.1中，在步骤3执行完成后，connection不再被关闭，在connection有效的前提细，后面client不再需要执行步骤1，直接执行步骤2、3就可以。为了进一步深入，如下图2，图2是我从国外的网上截下来的，建议读者阅读 图2 HttpSession生成后会有个sessionIDClient第一次发送请求，web container生成唯一的session ID(生成session ID的源码，如有兴趣，可以看下tomcat源码， 随机数+时间+jvmid)，并将其返回给client(在web container返回给client的response中)，web container上的这个HttpSession是临时的。 后面Client在每次发送请求给服务器时，都将session ID发送给web container，这样web container就很容易区分出是哪个client. Web container使用这个session ID，找到对应的HttpSession，并将此次request与这个HttpSession联系起来。 HttpSession生命周期：什么时候创建HttpSession1）.对于JSP： 是否浏览器访问服务端的任何一个JSP或Servlet，服务器都会立即创建一个HttpSession对象呢？ 不一定。 ①.若当前的JSP或（Servlet）是客户端访问的当前WEB应用的第一个资源，且JSP的page指定的session属性为false，则服务器就不会为JSP创建一个HttpSession对象； ②.若当前JSP不是客户端访问的当前WEB应用的第一个资源，且其他页面已经创建一个HttpSession对象，则服务器也不会为当前JSP创建一个新的HttpSession对象，而会把和当前会话关联的那个HttpSession对象返回给当前的JSP页面。2）.page指令的session="false"到底表示什么意思：当前JSP页面禁用session隐含变量！但可以使用其他的显式的对象3）.对于Servlet而言： 若Servlet是客户端访问的第一个WEB应用的资源，则只有调用了request.getSession()或request.getSession(true) 才会创建HttpSession对象4）. 在Servlet中如何获取HttpSession对象？ request.getSession(boolean create):create为false,若没有和当前JSP页面关联的HttpSession对象,则返回null； 若有返回true create为true一定返回一个HTTPSession对象。若没有和昂前JSP页面关联的HttpSession对象，则服务器创建一个新的HttpSession对象返回，若有，则直接返回关联。 request.getSession()等同于request.getSession(true)什么时候销毁HttpSession对象：1）.直接调用HttpSession的invalidate（）方法：使HttpSession失效2）.服务器卸载了当前Web应用。3）.超出HttpSession的过期时间。设置HttpSession的过期时间：单位为Ssession.setMaxInactiveInterval(5);out.print(session.getMaxInactiveInterval()); 30 由于会有越来越多的用户访问服务器，因此Session也会越来越多。为防止内存溢出，服务器会把长时间内没有活跃的Session从内存删除。这个时间就是Session的超时时间。如果超过了超时时间没访问过服务器，Session就自动失效了。Session具有以下特点：（1）Session中的数据保存在服务器端；（2）Session中可以保存任意类型的数据；（3）Session默认的生命周期是30分钟，可以手动设置更长或更短的时间。1：session进行身份验证的原理：当客户端第一次访问服务器的时候，此时客户端的请求中不携带任何标识给服务器，所以此时服务器无法找到与之对应的session，所以会新建session对象，当服务器进行响应的时候，服务器会将session标识放到响应头的Set-Cookie中，会以key-value的形式返回给客户端，例：JSESSIONID=7F149950097E7B5B41B390436497CD21；其中JSESSIONID是固定的，而后面的value值对应的则是给该客户端新创建的session的ID，之后浏览器再次进行服务器访问的时候，客户端会将此key-value放到cookie中一并请求服务器，服务器就会根据此ID寻找对应的session对象了；（当浏览器关闭后，会话结束，由于cookie消失所以对应的session对象标识消失，而对应的session依然存在，但已经成为报废数据等待GC回收了）对应session的ID可以利用此方法得到：session.getId();
查询对应jdk版本查询地址 https://www.elastic.co/cn/support/matrix#matrix_jvm
创建独立spring boot 应用引入依赖spring cloud spirng org.springframework.cloud spring-cloud-starter-consul-discovery 配置application.propertiesserver.port=8082指定服务名称spring.application.name=CONSULCLIENTconsul server 服务注册地址spring.cloud.consul.host=localhostspring.cloud.consul.port=8500spring.cloud.consul.discovery.service-name=${spring.application.name}加注解在入口类上面@EnableDiscoveryClient //通用服务注解启动成功后 连上服务会一直显示，红XX，添加下面健康检查依赖即可 org.springframework.boot spring-boot-starter-actuator
consul 服务注册中心 简介：consul 基于go 语言开发的服务注册中心 轻量级服务注册中心 google开发 作用: 管理微服务中所有的服务 注册 发现 管理 服务元数据信息存储（服务名 地址列表） 心跳健康检测consul 服务中心安装 下载地consulhttps://www.consul.io/downloads在指定目录解压，注意：不建议目录包含中文 E:\consul\consul.exe 启动服务注册中心 在consul 安装目录打开cmd consul agent -dev 访问consul 管理界面 浏览器输入:http://localhost:8500 管理界面基本介绍 dc1：数据中心名称：datacenter 默认为：dc1 指定数据中心启动 consul agent -dev -datacenter=sss services ：当前consul 服务中注册服务列表 ，默认:client server 同时启动自己注册自己 会出现一个consul 一个服务 nodes 查看集群的节点 key/value 存储 数据 ACL 版本集成控制 Intentions 集成其他第三方组件 推荐 配置环境变量path:E\consul(自己安装consul目录)
Java 多线程设计模式 —— Single Threaded Execution
Single Threaded Execution 有时也会被称之为 临界区。表示 临界区 内的代码同一时间内只允许一个线程执行，这个模式是并发编程的基础，对应 Java 中也就是同步代码块 synchronized，或其他显式锁 lock。多线程环境多个线程同时访问的共享资源这些线程会改变共享资源。示例现有票 100 张，分三个窗口售卖，卖完为止。票类:public class Ticket { private int counter = 100; public void sell() { if (counter > 0) { System.out.println(Thread.currentThread().getName() + "号窗口卖出：" + this.counter-- + "号票"); } else { System.out.println("票已卖完"); } } } 窗口类:public class StationThread extends Thread { private final Ticket ticket; public StationThread(String name, Ticket ticket) { super(name); this.ticket = ticket; } @Override public void run() { while (true) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } ticket.sell(); } } } 测试类:public class Main { public static void main(String[] args) { Ticket ticket = new Ticket(); new StationThread("S1", ticket).start(); new StationThread("S2", ticket).start(); new StationThread("S3", ticket).start(); } } 很明显的可以看到，同一张票，被多个窗口同时售卖了，就是因为未对共享资源 Ticket 做保护，导致了这种情况。解决办法也很简单，给 Ticket 的 sell 方法加上 synchronized 即可。这样就防止了多个窗口同时访问同一张票。public class Ticket { private int counter = 100; public synchronized void sell() { if (counter > 0) { System.out.println(Thread.currentThread().getName() + "号窗口卖出：" + this.counter-- + "号票"); } else { System.out.println("票已卖完"); } } } 类图时序图总结单线程的情况，不需要使用此模式，就好像一个人在家上厕所，不需要锁门一样。只有当 多线程 同时对 同一个共享资源，在线程中 进行了修改，才需要使用此模式。且需要注意，对于 共享资源，如果用到了此模式，要保证所有使用他的地方都进行了保护。不然等于你把门锁住了，但窗子没关。
集群实例集群搭建完全集群创建3个springboot项目 引入eureka server 依赖 配置文件application.properties tips：node为一个微小的springboot项目node1： server.port=8761 http://localhost:8762/eureka,http://localhost:8763/eurekanode2： server.port=8762 http://localhost:8761/eureka,http://localhost:8763/eurekanode3： server.port=8763 http://localhost:8761/eureka,http://localhost:8762/eureka 在每个项目main方法加入@EnableEurekaServer 注解
引入依赖 org.springframework.cloud spring-cloud-starter-netflix-eureka-client </dependency> 写配置#指定服务端口 server.port=8989 #指定 服务名称 spring.application.name=EUREKACLIENT #eureka server 服务注册中心地址 暴露服务地址 eureka.client.service-url.defaultZone=http://localhost:8761/eureka #用来修改eureka server 默认接受心跳最大时间 默认是90s eureka.instance.lease-expiration-duration-in-seconds=10 #指定客户端多久向eureka server 发送一次心跳 默认是30s eureka.instance.lease-renewal-interval-in-seconds=5 加注解Application main方法上面加上这个注解@EnableEurekaClient //让当前微服务作为一个eurekaserver客户端进行服务注册
引入Eureka依赖 org.springframework.cloud spring-cloud-starter-netflix-eureka-server 配置Eureka-server#eureka 默认端口 8761 server.port=8761 #指定服务名称 ps:服务不能出现下划线 默认服务名称不区分大小写，推荐服务大写 spring.application.name=EUREKASERVER #eureka server 服务注册中心地址 暴露服务地址 eureka.client.service-url.defaultZone=http://localhost:8761/eureka #关闭eureka client 立即注册 eureka.client.fetch-registry=false # 让当前应用仅仅是服务服务注册中心 eureka.client.register-with-eureka=false #eureka关闭自我保护机制 #关闭自我保护 eureka.server.enable-self-preservation=false #超时3s自动清除 eureka.server.eviction-interval-timer-in-ms=3000 写注解在application spring类上面加上注解@EnableEurekaServer //开启当前应用是一个服务注册中心
package cn.rivamed.config.security; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .anyRequest().permitAll() .and().logout().permitAll(); } }