java 代码
java 里的 pandas
tablesaw
DataFrame
再有就是 spark 了
java 代码规范
java 阿里规范
Java8特性详解 lambda表达式 Stream
Sonar 规则检测
springboot官方
java有时间没用就会容易生疏,这篇就来记录自己平时java的练习
推荐启动 jvm 时候 设置 classpath
java -cp all.jar com.ghc.Main
观察者模式
// 定义观察者接口
public interface Observer {
void callBack(String message,String name); // 调用MTB 接口发送数据过去
}
// 定义被观察者接口 (坏人)
public interface BadGuy {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notice(String message);
}
// 观察者实现类 警察 (其实这里也可以是线人所以定义 Observer 接口是有好处的)
public class Policeman implements Observer {
private String pName;
Policeman(String pName){
this.pName = pName;
}
@Override
public void callBack(String message, String name) {
System.out.println(this.pName+": "+name+" noticed:"+message);
}
}
// 被观察者 小偷(这里也可以是毒贩 所以定义 BadGuy 接口的好处也是显而易见的)
import java.util.concurrent.LinkedBlockingQueue;
public class Thief implements BadGuy {
private String name;
Thief(String name){
this.name = name;
}
private LinkedBlockingQueue<Observer> linkedBlockingQueue = new LinkedBlockingQueue<Observer>();
@Override
public void addObserver(Observer observer) {
if(!linkedBlockingQueue.contains(observer)){
try {
linkedBlockingQueue.put(observer);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void removeObserver(Observer observer) {
if(linkedBlockingQueue.contains(observer)){
linkedBlockingQueue.remove(observer);
}
}
@Override
public void notice(String message) {
for(Observer observer:linkedBlockingQueue){
observer.callBack(message,this.name);
}
}
}
//最后来个测试调用
public class ObserverDemo {
public static void main(String [] args){
Observer policeman = new Policeman("李警官");
BadGuy thief = new Thief("小偷甲");
thief.addObserver(policeman);
thief.notice("那里人傻钱多速来...");
}
}
java 排序算法实现 待完善。。。
package com.middleplugin;
/*
* 这个类是一个演示类所以取名叫 Demo
* 主要演示循环与选择分支以及数组这一重要容器的使用
* 涉及到常见排序算法:冒泡排序,快速排序*/
public class Demo {
public static void main(String[] args){
int [] array = {5,6,4,3,7,8,2};
System.out.print("冒泡排序前的数组打印结果:");
printArray(array); // 在排序之前打印数组 array
bubbleSort(array);//冒泡排序
quickSort(array,0,array.length-1); // 快速排序
System.out.println(); //打印空行为了
System.out.print("冒泡排完序的数组打印结果:");
printArray(array);// 在冒泡排序之后打印排好序的数组 array
System.out.println("\n----===========我是华丽的分割线===========----");
int [] secondArray = {5,6,4,3,7,8,2};
System.out.print("快速排序前的数组打印结果:");
printArray(secondArray); // 在排序之前打印数组 array
quickSort(secondArray,0,secondArray.length-1); // 快速排序
System.out.println(); //打印空行为了
System.out.print("快速排完序的数组打印结果:");
printArray(secondArray);// 在冒泡排序之后打印排好序的数组 array
System.out.println();
String [] names = {"Frank","May","Tom","Jim"};
for(String name:names){
//遍历 名字数组 names 注意这个是有 s 的
System.out.println(name);
}
}
// 这是一个格式化打印数组的方法,注意放在类里一般叫方法而不讲函数
public static void printArray(int [] array){
System.out.print("[");
for(int i=0;i<array.length;i++){
if(i!=array.length-1){
System.out.print(array[i]+",");
}
else{
System.out.print(array[i]+"]");
}
}
}
//交换数组值的方法
public static void swapArray(int[] array,int i,int j){
int temp = array[i];//使用第三方变量是常用的方法
array[i] = array[j];
array[j] = temp;
}
//冒泡排序算法的具体写法
public static void bubbleSort(int[] array){
for(int i=0;i<array.length;i++){
for(int j=0;j<array.length-1-i;j++){
if(array[j]>array[j+1]){
swapArray(array,j,j+1);
}
}
}
}
//快速排序算法,这个比较复杂一点,如果可以,画图理解比较好,主要采取分治策略
public static void quickSort(int[] array,int low,int high){
if(low>=high){
return; // 如果左边的指针往右移超过或者等于了右边的指针,那么跳出递归
}
int left = low;
int right = high;
int baseValue = array[left];
while(left<right){//开启循环递归
while(left<right&&array[right]>=baseValue){
right--; //如果右边指针左移 一直都是比基准值大或者相等的,则继续左移
}
// 跳出了上面的循环,那么意味着右边指针找到比基准值小的数了,这时候填左边坑
array[left] = array[right];
while(left<right&&array[left]<baseValue){
left++; //同理,左边指针右移 一直都是比基准值小的数字,则继续右移
}
//跳出了上面的循环,那么意味着左边指针找到比基准值大的数字了,这时候填右边坑
array[right] = array[left];
//一次循环结束,则要把基准值放入左边坑
array[left] = baseValue;
//开启递归
quickSort(array,low,left-1);
quickSort(array,left+1,high);
}
}
}
枚举类型
public enum WeekDayEnum {
MONDAY("星期一"),TUESDAY("星期二"),WENDESDAY("星期三"),THURSDAY("星期四"),FRIDAY("星期五"),SATURDAY("星期六"),SUNDAY("星期日");
private final String name;
WeekDayEnum(String name){
this.name = name;
}
public String getName(){
return this.name;
}
}
package com.ghc.hbase.api;
import java.util.Arrays;
public class Demo {
public static void main(String[] args){
WeekDayEnum weekDayEnum = WeekDayEnum.SATURDAY;
System.out.println(weekDayEnum.getName());
switch(weekDayEnum){
case MONDAY:
System.out.println(weekDayEnum.getName());
break;
case TUESDAY:
System.out.println(weekDayEnum.getName());
break;
case SATURDAY:
case SUNDAY:
System.out.println("周末");
break;
default:
System.out.println("略略略...");
break;
}
// 这 函数式编程简直上瘾。。。
Arrays.stream(WeekDayEnum.values()).forEach(System.out::println);
Arrays.stream(WeekDayEnum.values()).forEach(e->System.out.println(e));
}
}
/**
* jdk8函数式接口,SAM类型的接口(Single Abstract Method)
* 定义了这种类型的接口,使得以其为参数的方法,可以在调用时,使用一个lambda表达式作为参数
* 从SAM原则上讲,这个接口中,只能有一个函数需要被实现,但是也可以有如下例外:
* 1. 默认方法与静态方法并不影响函数式接口的契约,可以任意使用,即函数式接口中可以有静态方法,
* 一个或者多个静态方法不会影响SAM接口成为函数式接口,并且静态方法可以提供方法实现可以由 default 修饰的默认方法方法,
* 这个关键字是Java8中新增的,为的目的就是使得某一些接口,原则上只有一个方法被实现,但是由于历史原因,
* 不得不加入一些方法来兼容整个JDK中的API,所以就需要使用default关键字来定义这样的方法
* 2. 可以有 Object 中覆盖的方法,也就是 equals,toString,hashcode等方法。
* @author wind、
在java中函数式编程在两个地方使用起来很方便,一个是匿名函数,一个是集合的stream操作。
只有一个方法的接口使用 @FunctionalInterface 注解那他就是 匿名函数
*
*/
//函数式接口注解
@FunctionalInterface
interface AInterface{
String getName(String name);
}
public class FunInterface {
public static void testFun(AInterface a){
System.out.println(a.getName("AAA"));
}
public static void main(String[] args) {
//新方法
AInterface a = (name) -> "hello " + name;
System.out.println("a" + a.getName("world"));
//老方法
AInterface b = new AInterface() {
@Override
public String getName(String name) {
return "hello " + name;
}
};
System.out.println("b" + b.getName("world"));
testFun(val -> "hello=" + val);
}
}
// 几乎已经中了 函数式的 毒
private static boolean filterEven(int x){
return x%2==0;
}
public static void main(String[] args){
int [] indexes = {1,2,3,3,2,3,4,5,6,7};
System.out.println(Arrays.stream(indexes).map(num->{return (int)(Math.random()*2)+num;}).distinct().filter(Demo::filterEven).reduce((x,y)->x+y).getAsInt());
}
// 脑洞佛系排序大法,轻易不要用,被打了别怪我。。。
public static void sortPrintArray(int[] array){
Arrays.stream(array).forEach((n)->{new Thread(()->{
try{Thread.sleep(n);
System.out.println(n);
}catch(InterruptedException ie){
ie.printStackTrace();}}
).start();
});
}
通用接口返回优雅定义 (枚举类(传递状态码与状态信息)+泛型(传递数据))
/*枚举类*/
package com.middleplugin.result;
public enum CodeMsg {
SUCCESS("SUCCESS",200),FAILED("FAILED",500);
private final String name;
private final int status;
CodeMsg(String name,int status){
this.name = name;
this.status = status;
}
public String getName(){
return this.name();
}
public int getStatus(){
return this.status;
}
@Override
public String toString(){
return this.name+":"+this.status;
}
}
/*封装返回类 Result */
// --------------------==================---------------------
package com.middleplugin.result;
public class Result<T> {
private String code;
private int msg;
private T data;
public static <T> Result<T> success(T data){
return new Result<T>(data);
}
public static <T> Result<T> error(){
Result result = new Result<T>(null);
result.code = CodeMsg.FAILED.getName();
result.msg = CodeMsg.FAILED.getStatus();
return result;
}
private Result(T data){
this.data = data;
this.code = CodeMsg.SUCCESS.getName();
this.msg = CodeMsg.SUCCESS.getStatus();
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public int getMsg() {
return msg;
}
public void setMsg(int msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
// controller 层调用,当然 正常情况是丢到 catch exception 里 Result.error() 的
@RequestMapping(value="/Validate/GeneVariant3")
@ResponseBody
public Result<Boolean> GeneVariant3(@RequestBody String params){
String paramsStr = JSONObject.parseObject(params).getString("params");
if(StringUtils.isEmpty(paramsStr)){
return Result.error();
}
else{
return Result.success(geneVariantDistinctService.isInvalidate(ValidateUtils.splitStr2List(paramsStr,",")));
}
}
@RestController是@ResponseBody + @Controller合体,当你在这个controller中方法只是想返回一个页面时,就不能用@RestController,因为它会把你的返回值当作数据返回,而不是页面名字,所以这时候就只能用@Controller。
如果mvn比较慢,建议更改maven目录下的conf中的setting.xml,找到mirrors,在里面加入这段话
<id>nexus-aliyun</id>
<mirrorOf>*</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
构建最大堆
package com.ghc.starter.utils;
public class MaxHeapUtils {
public static int [] array = {1,2,4,3,6,8,5,7,9,10};
public static int heapSize = array.length;
public static void main(String [] args){
printArray(array);
for(int i=heapSize/2;i>0;i--){
maxHeapify(array,i);
}
printArray(array);
}
public static void printArray(int [] array){
System.out.print("[");
for(int i=0;i<array.length;i++){
if(i!=heapSize-1){
System.out.print(array[i]+",");
}else{
System.out.println(array[i]+"]");
}
}
}
public static void maxHeapify(int[] array,int rootIndx){
int l = 2*rootIndx;
int r = l+1;
int largest;
if(l<=heapSize && array[l-1]>array[rootIndx-1]){
largest = l;
}else{
largest = rootIndx;
}
if(r<=heapSize && array[r-1] > array[largest-1]){
largest = r;
}
// 只有当 largest 不是 index 时候才需要交换最大值与index所指向父节点的位置,然后递归调用该方法调整位置。
if(largest!=rootIndx){
swap(array,rootIndx-1,largest-1);
// 这里只有当 largest 没超过 arrayLength/2 的时候递归才有意义
if(largest<=heapSize/2) {
maxHeapify(array, largest);
}
}
}
public static void swap(int [] array,int i, int j){
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
基础
public class Demo {
static{
System.out.println("static code...");
}
public static void main(String [] args){
new Demo().run();
System.out.println("main code...");
}
{
System.out.println("non-static code...");
}
public void run(){
System.out.println("run code...");
}
}
结果:
static code...
non-static code...
run code...
main code...
优化循环
for(int i=0, n=10;i<=n;i++){
System.out.println(i);
}
设置Mybatis打印调试sql的两种方式
http://blog.csdn.net/gao36951/article/details/53641432
**************************************************
问题描述
在使用mybatis进行开发的时候,由于可以动态拼接sql,这样大大方便了我们。但是也有一定的问题,当我们动态sql拼接的块很多的时候,我们要想从*mapper.xml中直接找出完整的sql就会非常的难,这个时候经常会需要把组合之后的完整sql调试出来比较好。下面来看两种调试出sql的两种方式
解决方案
方案1:
网上说的比较多的,之前也是这么用的一种方式
1:首先将ibatis log4j运行级别调到DEBUG可以在控制台打印出ibatis运行的sql语句
2:添加如下语句
按 Ctrl+C 复制代码
###显示SQL语句部分
log4j.logger.com.ibatis=DEBUG
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG
log4j.logger.Java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
按 Ctrl+C 复制代码
方案2:
最近发现的一种方式,方便快捷
在mybatis.cfg.xml中增加如下配置
<settings>中增加
<setting name="logImpl" value="STDOUT_LOGGING" />
以上mybatis 调试出sql的两种方式,Mark~~~
补充:
在spring boot 中,可以配置application.properties
# --- {Logging}
logging.level.com.neusoft.newsroom=INFO
logging.level.org.springframework.security=INFO
logging.level.org.hibernate=ERROR
logging.level.com.mypackage.domain.persistence=DEBUG
Springboot + Druid 连接池
Druid是什么?
Druid是Java语言中最好的数据库连接池。对于一个简单的数据库应用,由于对于数据库的访问不是很频繁。这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什么明显的性能上的开销。但是对于一个复杂的数据库应用,情况就完全不同了。频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。这个时候,我们通过建立一个数据库连接池以及一套连接使用管理策略,使得一个数据库连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。
那么,我们在springboot程序当中,如何配置druid数据库连接池呢?
第一步:maven引入相应的jar包。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.5</version>
</dependency>
第二步:在resource下面,新增application.properties文件,添加数据库的访问配置
# 数据库访问配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=123456
# 下面为连接池的补充设置,应用到上面所有数据源中
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j
spring.datasource.logSlowSql=true
第三步:由于springboot程序的特性,我们在工程的config包下面,创建DruidConfig.java文件,配置数据库连接池。
第一种的配置方式:
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.util.StringUtils;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.sql.SQLException;
import java.util.Arrays;
@Configuration
@EnableTransactionManagement
public class DuridConfig implements EnvironmentAware {
private static final Logger logger = LoggerFactory.getLogger(DuridConfig.class);
private Environment environment;
private RelaxedPropertyResolver propertyResolver;
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
this.propertyResolver = new RelaxedPropertyResolver(environment, "spring.datasource.");
}
//注册dataSource
@Bean(initMethod = "init", destroyMethod = "close")
public DruidDataSource dataSource() throws SQLException {
if (StringUtils.isEmpty(propertyResolver.getProperty("url"))) {
System.out.println("Your database connectio n pool configuration is incorrect!"
+ " Please check your Spring profile, current profiles are:"
+ Arrays.toString(environment.getActiveProfiles()));
throw new ApplicationContextException(
"Database connection pool is not configured correctly");
}
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(propertyResolver.getProperty("driver-class-name"));
druidDataSource.setUrl(propertyResolver.getProperty("url"));
druidDataSource.setUsername(propertyResolver.getProperty("username"));
druidDataSource.setPassword(propertyResolver.getProperty("password"));
druidDataSource.setInitialSize(Integer.parseInt(propertyResolver.getProperty("initialSize")));
druidDataSource.setMinIdle(Integer.parseInt(propertyResolver.getProperty("minIdle")));
druidDataSource.setMaxActive(Integer.parseInt(propertyResolver.getProperty("maxActive")));
druidDataSource.setMaxWait(Integer.parseInt(propertyResolver.getProperty("maxWait")));
druidDataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(propertyResolver.getProperty("timeBetweenEvictionRunsMillis")));
druidDataSource.setMinEvictableIdleTimeMillis(Long.parseLong(propertyResolver.getProperty("minEvictableIdleTimeMillis")));
druidDataSource.setValidationQuery(propertyResolver.getProperty("validationQuery"));
druidDataSource.setTestWhileIdle(Boolean.parseBoolean(propertyResolver.getProperty("testWhileIdle")));
druidDataSource.setTestOnBorrow(Boolean.parseBoolean(propertyResolver.getProperty("testOnBorrow")));
druidDataSource.setTestOnReturn(Boolean.parseBoolean(propertyResolver.getProperty("testOnReturn")));
druidDataSource.setPoolPreparedStatements(Boolean.parseBoolean(propertyResolver.getProperty("poolPreparedStatements")));
druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(Integer.parseInt(propertyResolver.getProperty("maxPoolPreparedStatementPerConnectionSize")));
druidDataSource.setFilters(propertyResolver.getProperty("filters"));
logger.info("DuridConfig-----数据源完成");
return druidDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
// //mybatis分页
// PageHelper pageHelper = new PageHelper();
// Properties props = new Properties();
// props.setProperty("dialect", "mysql");
// props.setProperty("reasonable", "true");
// props.setProperty("supportMethodsArguments", "true");
// props.setProperty("returnPageInfo", "check");
// props.setProperty("params", "count=countSql");
// pageHelper.setProperties(props);
// //添加插件
// sqlSessionFactoryBean.setPlugins(new Interceptor[]{pageHelper});
//添加XML目录
VFS.addImplClass(SpringBootVFS.class);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
logger.info("dao层扫描包为:mapper/*.xml");
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager() throws SQLException {
return new DataSourceTransactionManager(dataSource());
}
}
第二种的配置方式:
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.github.pagehelper.PageHelper;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Properties;
/**
* _ooOoo_
* o8888888o
* 88" . "88
* (| -_- |)
* O\ = /O
* ____/`---'\____
* .' \\| |// `.
* / \\||| : |||// \
* / _||||| -:- |||||- \
* | | \\\ - /// | |
* | \_| ''\---/'' | |
* \ .-\__ `-` ___/-. /
* ___`. .' /--.--\ `. . __
* ."" '< `.___\_<|>_/___.' >'"".
* | | : `- \`.;`\ _ /`;.`/ - ` : | |
* \ \ `-. \_ __\ /__ _/ .-` / /
* ======`-.____`-.___\_____/___.-`____.-'======
* `=---='
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* 佛祖保佑 永无BUG
* 佛曰:
* 写字楼里写字间,写字间里程序员;
* 程序人员写程序,又拿程序换酒钱。
* 酒醒只在网上坐,酒醉还来网下眠;
* 酒醉酒醒日复日,网上网下年复年。
* 但愿老死电脑间,不愿鞠躬老板前;
* 奔驰宝马贵者趣,公交自行程序员。
* 别人笑我忒疯癫,我笑自己命太贱;
* 不见满街漂亮妹,哪个归得程序员?
*/
@Configuration
@EnableTransactionManagement
public class DruidConfig {
private Logger logger = LoggerFactory.getLogger(DruidConfig.class);
@Value("${spring.datasource.url}")
private String dbUrl;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.initialSize}")
private int initialSize;
@Value("${spring.datasource.minIdle}")
private int minIdle;
@Value("${spring.datasource.maxActive}")
private int maxActive;
@Value("${spring.datasource.maxWait}")
private int maxWait;
@Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.validationQuery}")
private String validationQuery;
@Value("${spring.datasource.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.testOnReturn}")
private boolean testOnReturn;
@Value("${spring.datasource.filters}")
private String filters;
@Value("${spring.datasource.logSlowSql}")
private String logSlowSql;
@Bean
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*");
reg.addInitParameter("loginUsername", username);
reg.addInitParameter("loginPassword", password);
reg.addInitParameter("logSlowSql", logSlowSql);
return reg;
}
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
filterRegistrationBean.addInitParameter("profileEnable", "true");
return filterRegistrationBean;
}
@Bean
public DataSource druidDataSource() {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(dbUrl);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
datasource.setInitialSize(initialSize);
datasource.setMinIdle(minIdle);
datasource.setMaxActive(maxActive);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
try {
datasource.setFilters(filters);
} catch (SQLException e) {
logger.error("druid configuration initialization filter", e);
}
return datasource;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(druidDataSource());
//mybatis分页
PageHelper pageHelper = new PageHelper();
Properties props = new Properties();
props.setProperty("dialect", "mysql");
props.setProperty("reasonable", "true");
props.setProperty("supportMethodsArguments", "true");
props.setProperty("returnPageInfo", "check");
props.setProperty("params", "count=countSql");
pageHelper.setProperties(props);
//添加插件
sqlSessionFactoryBean.setPlugins(new Interceptor[]{pageHelper});
//添加XML目录
VFS.addImplClass(SpringBootVFS.class);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
logger.info("dao层扫描包为:mapper/*.xml");
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager() throws SQLException {
return new DataSourceTransactionManager(druidDataSource());
}
}
需要的jar包的信息都包含在import信息当中。
通过以上的三步操作,我们就已经配置好了JAVA当中最好的数据库连接池了。
使用阿里云代码规范插件扫描后出现以下提示:
hashmap should set a size when initalizing,即hashmap应该在初始化时设置一个大小
在网上搜到一篇讲解(https://www.cnblogs.com/coderxuyang/p/3718856.html),如下:
在元素的装载数量明确的时候HashMap的大小应该如何选择。
今天看到美团招聘给出了一道小题目,关于HashMap的性能问题。问题如下:
java hashmap,如果确定只装载100个元素,new HashMap(?)多少是最佳的,why?
要回答这个问题,首先得知道影响HashMap性能的参数有哪些。咱们翻翻JDK。
在JDK6中是这么描述的:
HashMap的实例有两个参数影响其性能:初始容量和加载因子。
首先我们来看初始容量和加载因子的定义。
容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。
加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。
当哈希表中条目的数目超过 容量乘加载因子 的时候,则要对该哈希表进行rehash操作,从而哈希表将具有大约两倍的桶数。(以上摘自JDK6)
HashMap默认的加载因子是0.75 .它在时间和空间成本上寻求了一种折中。
回到本文的问题。根据JDK中的描述,如果这个只装载100个元素的HashMap没有特殊的用途,那么为了在时间和空间上达到最佳性能,HashMap的初始容量可以设为
100/0.75 = 133.33。为了防止rehash,向上取整,为134。
但是还有另外一个问题,就是hash碰撞的问题。如果我们将HashMap的容量设置为134,那么如何保证其中的哈希碰撞会比较少呢?
除非重写hashcode()方法,否则,似乎没有办法保证。
那么这里不得不提HashMap如何为元素选择下标的方法了。
static int indexFor(int h, int length) {
return h & (length-1);
}
其中h为key哈希后得到的值,length为哈希表的长度。
134-1 = 128 + 6 -1;
那么 length-1的二进制值的最后3位为101;
假设这100个装载的元素中他们的key在哈希后有得到两个值(h),他们的二进制值除了低3位之外都相同,而第一个值的低3位为011,第二个值的低3位为001;
这时候进行java的&预算,011 & 101 = 001 ;001 & 101 = 001;
他们的值相等了,那么这个时候就会发生哈希碰撞。
除此之外还有一个更加严重的问题,由于在101中第二位是0,那么,无论我们的key在哈希运算之后得到的值h是什么,那么在&运算之后,得到的结果的倒数第二位均为0;
因此,对于hash表所有下标的二进制的值而言,只要低位第二位的值为1,(例如0010,0011,0111,1111)那么这个下标所代表的桶将一直是空的,因为代码中的&运算的结果永远不会产生低位第二位为1的值。这就大大地浪费了空间,同时还增加了哈希碰撞的概率。这无疑会降低HashMap的效率。
那么如何才能减少这种浪费呢?最佳的方法当然是让length-1的二进制值全部位均为1.那么length的值是多少合适呢?
没错,length=2^n。
只要将hash表的长度设为2的N次方,那么,所有的哈希桶均有被使用的可能。
再次回到美团提出的问题,与134最靠近的2^n无疑是128。
如果只修改HashMap的长度而不修改HashMap的加载因子的话,HashMap会进行rehash操作,这是一个代价很大的操作,所以不可取。
那么应该选择的就应该是256。
而由于空间加大和有效利用哈希桶,这时的哈希碰撞将大大降低,因此HashMap的读取效率会比较高。
所以,最后结论就是:HashMap的大小应该设置为256。
结果的补充:其实在Java中,无论你的HashMap(x)中的x设置为多少,HashMap的大小都是2^n。2^n是大于x的第一个数。因为HashMap的初始化代码中有以下这行代码:
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
但是这就带来了一个问题,如果x=100,那么HashMap的初始大小应该是128.但是100/128=0.78,已经超过默认加载因子(0.75)的大小了。因此会resize一次,变成256。所以最好的结果还是256。
最后发个参考链接:http://www.iteye.com/topic/539465
另,总结StringBuffer、ArrayList、HashMap的扩容:
StringBuffer:内部实现是一个字符数组。初始默认大小为16,当然也可以在其构造方法中进行设置。当新添加字符或字符串时,发现数组容量不够。这个时候就需要使用Array.copyOf()方法进行扩充。扩充的新的数组大小等于,(原始容量*2+2)和(数组实际字符个数+新增的字符长度)之间的较大值。
ArrayList:内部实现是一个Object的数组。初始默认大小为0,当然也可以在其构造方法中设置。当添加一个Object时,默认扩充数组容量为10。然后每次扩充的新的数组大小等于,(原始容量*3/2)和(数组的长度+1)之间的较大值。根据每次增加一个Object,可得该情况每次扩充的固定大小为3/2。当初始大小为手动设置的时候,每次扩充的新的数组大小等于,(原始容量*3/2)和(数组的长度+1)之间的较大值。
HashMap:内部实现是一个Entry的数组,默认大小是空的数组。初始化的容量是16,加载因子是3/4(当数组元素数量大于总容量的加载因子的时候,扩充数组)。当默认不是空的数组时,当达到加载因子的比例的时候,每次扩充初始容量的2倍。
自定义注解使用
import java.lang.annotation.*;
//元注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String desc();
String author();
int age() default 18;
}
// interface 其实 不会起作用应该 换成 class
public interface Person {
@Description(desc="eat",author = "Frank",age=20)
void eat();
void run();
@Deprecated
void sing();
}
Class c = Class.forName("com.ghc.hbase.api.Person");
boolean isExist = c.isAnnotationPresent(Description.class);
if(isExist){
Description d = (Description) c.getAnnotation(Description.class);
System.out.println(d.desc());
}
Method[] methods = c.getMethods();
for(Method m:methods){
boolean isExistOnMethod = m.isAnnotationPresent(Description.class);
if(isExistOnMethod){
Description d = (Description)m.getAnnotation(Description.class);
System.out.println(d.age());
}
}
for(Method m:methods){
Annotation[] annotations = m.getAnnotations();
for(Annotation annotation:annotations){
if(annotation instanceof Description){
((Description) annotation).author();
}
}
}
使用 数组 绕过 lambda 表达式 跟 匿名函数不能在内部 修改 外部变量的问题
Integer variantImpact;
final int[] variantImpactArray = new int[1];
VariantImpactEnum.getMap().keySet().forEach(key->{
if(features.contains(key)){
variantImpactArray[0] = VariantImpactEnum.getMap().get(key);
}
});
variantImpact = variantImpactArray[0];
集合操作排序 重复值问题
import java.util.Objects;
/**
* @author :Frank Li
* @date :Created in 2019/7/3 9:48
* @description:${description}
* @modified By:
* @version: $version$
*/
public class Student {
private String name;
private String gender;
private int age;
Student(String name, String gender, int age){
this.name = name;
this.gender = gender;
this.age = age;
}
@Override
public boolean equals(Object obj){
if(this == obj) return true;
if(!(obj instanceof Student)) return false;
Student that = (Student) obj;
return Objects.equals(this.name,that.name) && Objects.equals(this.gender,that.gender) && this.age==that.age;
}
@Override
public int hashCode(){
return Objects.hash(this.name,this.gender,this.age);
}
public String getName() {
return name;
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append(this.name)
.append("|")
.append(this.gender)
.append("|")
.append(this.age);
return sb.toString();
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
import java.util.*;
/**
* @author :Frank Li
* @date :Created in 2019/7/3 9:22
* @description:${description}
* @modified By:
* @version: $version$
*/
public class Demo {
public static void main(String [] args){
Set<Student> personSet = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return (o1.getAge()-o2.getAge());
}
});
Student s1 = new Student("a","f",18);
Student s2 = new Student("b","m",19);
Student s3 = new Student("c","f",20);
Student s4 = new Student("c","f",20);
personSet.add(s1);
personSet.add(s2);
personSet.add(s3);
personSet.add(s4);
for(Student s:personSet){
System.out.println(s);
}
System.out.println("**********");
List<Student> studentList = new ArrayList<Student>(5);
studentList.add(s1);
studentList.add(s2);
studentList.add(s3);
studentList.add(s4);
Collections.sort(studentList, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return -(o1.getAge() - o2.getAge());
}
});
System.out.println(studentList.toString());
for(Student s: dropDuplicateList(studentList)){
System.out.println(s);
}
}
public static <T extends Student> List<T> dropDuplicateList(List<T> inputList){
Set<T> dropDupSet = new TreeSet<T>(new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
return o1.getAge() - o2.getAge();
}
});
dropDupSet.addAll(inputList);
return new ArrayList<T>(dropDupSet);
}
}
// 优先级队列
Queue<Student> queue = new PriorityQueue<Student>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return - (o1.getAge() - o2.getAge());
}
});
queue.add(new Student("a","f",18));
queue.add(new Student("b","m",19));
queue.add(new Student("c","f",20));
for(int i=0, n=queue.size();i<n;i++){
Student s = queue.poll();
System.out.println(s);
}
// 双向队列
public static void main(String [] args){
Deque<Student> deque = new LinkedList<Student>();
Student s1 = new Student("a","f",18);
Student s2 = new Student("b","m",19);
Student s3 = new Student("c","f",20);
deque.addFirst(s2);
deque.addLast(s3);
deque.addFirst(s1);
for(int i=0, n=deque.size();i<n;i++){
Student s = deque.pollLast();
System.out.println(s);
}
}
常用工具类
public static void main(String [] args){
System.out.println(getRange(10,100).intValue());
}
public static Number getRange(int min, int max){
return Math.random()*(max-min)+min;
}
断言, IDE 中 配置 -ea:com.ghc.Demo
public static void main(String [] args){
System.out.println(getRange(10,100).intValue());
int randInt = 9; //getRange(10,100).intValue();
assert randInt >= 10 && randInt <100 : "rand Int should between "+10+" and "+100+" but randInt ="+randInt;
}
public static Number getRange(int min, int max){
return Math.random()*(max-min)+min;
}
等待研究 链接
Serializable
Transient
Vilotile
JVM
或许是惯性思维,在mybatis使用foreach循环调用的时候,很多时候都是传一个对象,传一个List的情况很少,所以写代码有时候会不注意就用惯性思维方法做了。
今天向sql传参,传了一个List作为参数,然后在xml里再foreach循环调用。然后报错信息如:
mybatis foreach报错It was either not specified and/or could not be found for the javaType
Type handler was null on parameter mapping for property '__flowStepCode_0
Mapper接口
List<AllocationHandlerInfoVo> listAllocatedHandlerInfo(@Param("flowStepCodeList")List<ApprStepModel> flowStepCodeList);
原来是这样:#{flowStep},处理方法,换成#{flowStep.flowStepCode},List是个集合来的,要注意,写博客记录
<if test="flowStepCodeList != null and flowStepCodeList.size() > 0">
fh.flow_step_code in
<foreach collection="flowStepCodeList" item="flowStep" index="index" open="(" close=")" separator=",">
#{flowStep.flowStepCode}
</foreach>
</if>
Map & Set 判断 元素是否相等 调用 hashCode + equals 方法一起
import java.util.HashMap;
import java.util.Map;
public class Person {
private String name;
private int age=0;
private String sex;
@Override
public int hashCode(){
return this.age;
}
public boolean equals(Object obj){
Person person = (Person) obj;
return this.name == person.name;
}
public Person(String name, int age, String sex){
this.name = name;
this.age = age;
this.sex = sex;
}
public static void main(String [] args){
Map map = new HashMap();
Person p1 = new Person("王恒定",20,"男");
Person p2 = new Person("刘炎",20,"女");
Person p3 = new Person("陈世阳",25,"女");
Person p4 = new Person("刘炎",20,"男");
map.put(p1,"教师");
map.put(p2,"特级教师");
map.put(p3,"教师");
map.put(p4,"高级教师");
System.out.println(map.size());
System.out.println(p2 == p4);
System.out.println(p2.equals(p4));
System.out.println(map.get(p2));
System.out.println(map.get(p4));
}
}
// 结果 p4 替换了 p2
正例:(涉及1-5点)
public static void main(String args[]) {
//缩进4个空格
String say = "hello";
//运算符的左右必须有一个空格
int flag = 0;
//关键词if与括号之间必须有一个空格,括号内的f与左括号,0与右括号不需要空格
if (flag == 0) {
System.out.println(say);
}
//左大括号前加空格且不换行;左大括号后换行
if (flag == 1) {
System.out.println("world");
//右大括号前换行,右大括号后有else,不用换行
} else {
System.out.println("ok");
//在右大括号后直接结束,则必须换行
}
}
import java.util.Objects;
使用 Objects.equals(obj1, obj2); // 来避免 equals 的问题
19.*【推荐】*慎用Object的clone方法来拷贝对象。
说明:对象的clone方法默认是浅拷贝,若想实现深拷贝需要重写clone方法实现属性对象的拷贝。
【推荐】*类成员与方法访问控制从严:
如果不允许外部直接通过new来创建对象,那么构造方法必须是private。
工具类不允许有public或default构造方法。
类非static成员变量并且与子类共享,必须是protected。
类非static成员变量并且仅在本类使用,必须是private。
类static成员变量如果仅在本类使用,必须是private。
若是static成员变量,必须考虑是否为final。
类成员方法只供类内部调用,必须是private。
类成员方法只对继承类公开,那么限制为protected
说明:任何类、方法、参数、变量,严控访问范围。过宽泛的访问范围,不利于模块解耦。
思考:如果是一个private的方法,想删除就删除,可是一个public的Service方法,或者一个public的成员变量,删除一下,不得手心冒点汗吗?变量像自己的小孩,尽量在自己的视线内,变量作用域太大,如果无限制的到处跑,那么你会担心的。
1.**【强制】**关于hashCode和equals的处理,遵循如下规则:
只要重写equals,就必须重写hashCode。
因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法。
如果自定义对象做为Map的键,那么必须重写hashCode和equals。
正例:String重写了hashCode和equals方法,所以我们可以非常愉快地使用String对象作为key来使用。
List<String> myList = new ArrayList<String>();
myList.add("a");
myList.add("b");
myList.add("c");
System.out.println(myList.subList(2,3) instanceof ArrayList);//false
System.out.println(myList.subList(2,3) instanceof List);//true
在subList场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均产生ConcurrentModificationException异常。
【强制】**使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一样的数组,大小就是list.size()。
反例:直接使用toArray无参方法存在问题,此方法返回值只能是Object[]类,若强转其它类型数组将出现ClassCastException错误。
正例:
List<String> list = new ArrayList<String>(2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);
说明:使用toArray带参方法,入参分配的数组空间不够大时,toArray方法内部将重新分配
内存空间,并返回新数组地址;如果数组元素大于实际所需,下标为[ list.size() ]的数组
元素将被置为null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素
个数一致。
【强制】**使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常。
说明:asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。
String[] str = new String[] { "a", "b" };
List list = Arrays.asList(str);
第一种情况:list.add("c");运行时异常。
第二种情况:str[0]= "gujin";那么list.get(0)也会随之修改。
【强制】**泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用add方法。
说明:苹果装箱后返回一个<? extends Fruits>对象,此对象就不能往里加任何水果,包括苹果。
9.*【推荐】*集合初始化时,尽量指定集合初始值大小。
说明:ArrayList尽量使用ArrayList(int initialCapacity)初始化。
10.*【推荐】*使用entrySet遍历Map类集合KV,而不是keySet方式进行遍历。
说明:keySet其实是遍历了2次,一次是转为Iterator对象,另一次是从hashMap中取出key所对应的value。而entrySet只是遍历了一次就把key和value都放到了entry中,效率更高。如果是JDK8,使用Map.foreach方法。
正例:values()返回的是V值集合,是一个list集合对象;keySet()返回的是K值集合,是一个Set集合对象;entrySet()返回的是K-V值组合集合。
11.*【推荐】*高度注意Map类集合K/V能不能存储null值的情况,如下表格:
集合类 Key Value Super 说明
Hashtable 不允许为null 不允许为null Dictionary 线程安全
ConcurrentHashMap 不允许为null 不允许为null AbstractMap 分段锁技术
TreeMap 不允许为null 允许为null AbstractMap 线程不安全
HashMap 允许为null 允许为null AbstractMap 线程不安全
反例:由于HashMap的干扰,很多人认为ConcurrentHashMap是可以置入null值,注意存储null值时会抛出NPE异常。
12.【参考】合理利用好集合的有序性(sort)和稳定性(order),避免集合的无序性(unsort)和不稳定性(unorder)带来的负面影响。
说明:稳定性指集合每次遍历的元素次序是一定的。有序性是指遍历的结果是按某种比较规则依次排列的。如:ArrayList是order/unsort;HashMap是unorder/unsort;TreeSet是order/sort。
13.【参考】利用Set元素唯一的特性,可以快速对一个集合进行去重操作,避免使用List的contains方法进行遍历、对比、去重操作。
(六) 并发处理
1.**【强制】**获取单例对象需要保证线程安全,其中的方法也要保证线程安全。
说明:资源驱动类、工具类、单例工厂类都需要注意。
2.**【强制】**创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
正例:
public class TimerTaskThread extends Thread {
public TimerTaskThread(){
super.setName("TimerTaskThread"); ...
}
3.**【强制】**线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
4.**【强制】**线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool:
允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool和ScheduledThreadPool:
允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
5.**【强制】**SimpleDateFormat是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类。
正例:注意线程安全,使用DateUtils。亦推荐如下处理:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
说明:如果是JDK8的应用,可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替Simpledateformatter,
官方给出的解释:simple beautiful strong immutable thread-safe。
6.**【强制】**高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
7.**【强制】**对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。
说明:线程一需要对表A、B、C依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序也必须是A、B、C,否则可能出现死锁。
8.**【强制】**并发修改同一记录时,避免更新丢失,要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用version作为更新依据。
说明:如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于3次。
9.**【强制】**多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题。
10.*【推荐】*使用CountDownLatch进行异步转同步操作,每个线程退出前必须调用countDown方法,线程执行代码注意catch异常,确保countDown方法可以执行,避免主线程无法执行至countDown方法,直到超时才返回结果。
说明:注意,子线程抛出异常堆栈,不能在主线程try-catch到。
11.*【推荐】*避免Random实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一seed导致的性能下降。
说明:Random实例包括java.util.Random的实例或者 Math.random()实例。
正例:在JDK7之后,可以直接使用API ThreadLocalRandom,在 JDK7之前,可以做到每个线程一个实例。
12.*【推荐】*通过双重检查锁(double-checked locking)(在并发场景)实现延迟初始化的优化问题隐患(可参考 The "Double-Checked Locking is Broken" Declaration),推荐问题解决方案中较为简单一种(适用于JDK5及以上版本),将目标属性声明为 volatile型。
反例:
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) synchronized(this) {
if (helper == null)
helper = new Helper();
}
return helper;
}
// other functions and members...
}
13.【参考】volatile解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。如果是count++操作,使用如下类实现:AtomicInteger count = new AtomicInteger(); count.addAndGet(1);如果是JDK8,推荐使用LongAdder对象,比AtomicLong性能更好(减少乐观锁的重试次数)。
14.【参考】 HashMap在容量不够进行resize时由于高并发可能出现死链,导致CPU飙升,在开发过程中注意规避此风险。
15.【参考】ThreadLocal无法解决共享对象的更新问题,ThreadLocal对象建议使用static修饰。这个变量是针对一个线程内所有操作共有的,所以设置为静态变量,所有此类实例共享此静态变量,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象(只要是这个线程内定义的)都可以操控这个变量。
public enum WeekEnum {
Monday(1,"周一"),
Tuesday(2,"周二"),
Wednesday(3,"周三");
int idx;
String description;
WeekEnum(int idx, String description){
this.idx = idx;
this.description = description;
}
}
WeekEnum mond = WeekEnum.Monday;
switch(mond){
case Monday:
System.out.println("monday...");
break;
case Tuesday:
System.out.println("Tuesday...");
break;
case Wednesday:
System.out.println("Wednesday...");
break;
default:
System.out.println("others....");
break;
}
Java编程语言(第三版)---Java四大名著----James Gosling(Java之父)
Java编程思想(第4版)----Java四大名著----------------Bruce Eckel
JAVA 2核心技术 卷I:基础知识(原书第8版)---Java四大名著-----Cay Horstmann
JAVA 2核心技术 卷II:高级特性(原书第8版)----Java四大名著-----Cay Horstmann
Effective Java中文版------Java四大名著--------Joshua Bloch
在线查看effectiv java
如果有来生,一个人去远行,看不同的风景,感受生命的活力。。。