Linux上开发常见问题整理
①首先要有一个main方法作为主类,程序的入口,右键-->Run As-->javaapplication生成配置文件入口
②右键该工程-->Export-->Runnable JAR file-->选择上一步生成的配置文件作为该jar运行入口,确定即可
③运行java -jar jar名+后缀(ps –ef|grep job)
总结:Linux后台运行Jar方法
· 在linux服务器上运行Jar文件时通常的方法是:
$ java -jar test.jar
· 这种方式特点是ssh窗口关闭时,程序中止运行.或者是运行时没法切出去执行其他任务,有没有办法让Jar在后台运行呢:
· 方法一:
$ nohup java -jar test.jar &
//nohup 意思是不挂断运行命令,当账户退出或终端关闭时,程序仍然运行
//当用 nohup 命令执行作业时,缺省情况下该作业的所有输出被重定向到nohup.out的文件中
//除非另外指定了输出文件。
· 方法二:
$ nohup java -jar test.jar >temp.txt &
//这种方法会把日志文件输入到你指定的文件中,没有则会自动创建
· jobs命令和 fg命令:
$ jobs
//那么就会列出所有后台执行的作业,并且每个作业前面都有个编号。
//如果想将某个作业调回前台控制,只需要 fg + 编号即可。
$ fg 2
· 查看某端口占用的线程的pid
netstat -nlp |grep :8080
①实时查看日志:tail –f 日志文件名称+后缀 (如:tail –f cat.out)
②查看某程序运行状况:ps –ef|grep 程序名 (如: ps –ef|grep redis)
./jdk-6u13-linux-i586.bin:./install.sfx.1956: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory Failed to extract the files.Please refer to the Troubleshooting section of the Installation Instructions onthe download p…
解答:Linux操作系统是64位的。软件包jdk-6u13-linux-i586.bin的名字中包含了i586,表示,他是32位的软件。将jdk改成64位的重新安装,错误解决。
// 为calendar实例设置小时为当前小时减一
calendar.set(Calendar.HOUR_OF_DAY,calendar.get(Calendar.HOUR_OF_DAY) - 1);
// HBase正则匹配
RegexStringComparatorcomp = newRegexStringComparator("20171116\\d|$");
filters.addFilter(newSingleColumnValueFilter(Bytes.toBytes("info"), Bytes.toBytes("time"), CompareOp.EQUAL, comp ));
①单个值的map排序
把每一个map放到list中,比较map的key或value,冒泡时整体移动
// 以时间冒泡排序
for (inti = 0; i < mapList.size() - 1; i++) {
for (int j = 0; j < mapList.size() - i - 1; j++) {
int a = Integer.valueOf(mapList.get(j).get("time").toString());
int b = Integer.valueOf(mapList.get(j+1).get("time").toString());
if(a > b){
Map
mapList.set((j + 1),mapList.get(j));
mapList.set(j, temp);
}
}
}
②多个值的map按value大小排序
publicclass MapSortUtil {
/**
* 按map值大小降序排列(float类型)
* @param oriMap
* @return
*/
publicstatic Map
if (oriMap == null || oriMap.isEmpty()) {
returnnull;
}
Map
List
oriMap.entrySet());
Collections.sort(entryList, new MapValueComparator());
Iterator
Map.Entry
while (iter.hasNext()) {
tmpEntry = iter.next();
sortedMap.put(tmpEntry.getKey(),tmpEntry.getValue());
}
return sortedMap;
}
}
publicclassMapValueComparator implements Comparator
@Override
publicintcompare(java.util.Map.Entry
returno2.getValue().compareTo(o1.getValue()); //降序
}
}
解决方案:找到被锁定的文件/文件夹 右键-->term--> Refresh/Cleanup清除锁即可更新/提交
①方法一:(float) Math.round(a*100)/100 //a是float类型
②方法二:
BigDecimal bg = new BigDecimal(f);
double f1 = bg.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
③方法三:
float num= (float)2/3;
DecimalFormat df = new DecimalFormat("0.00");//格式化小数
String s = df.format(num);//返回的是String类型
①进入kafka
cd kafka_2.10-0.10.0.0 (/home/hxxtsx/kafka_2.10-0.10.0.0)
②启动客户端
bin/kafka-console-producer.sh
③创建kafka消息和主题
--broker-listNM-304-SA5212M4-BIGDATA-659:9091 --topic systemlog --producer.configconfig/producer.properties
具体操作:
bin/kafka-console-producer.sh --broker-listNM-304-SA5212M4-BIGDATA-659:9091 --topic systemlog --producer.configconfig/producer.properties
④设置json格式消息
{"system":"scheduler","module":"listener","host":"192.168.1.1","comm":"hello! this isalert!","logTime":"20171123102333","logType":"system","flag":"Exception"}
语法:scan '表名',{FILTER => “ColumnPrefixFilter('列名') AND ValueFilter( = , 'substring:列的值')”}
示例:scan 'log:analysis_notice_state',{FILTER =>"ColumnPrefixFilter('workerID') AND ValueFilter ( = , 'substring:W111')"}
SingleColumnValueFilter:用一列的值决定这一行的数据是否被过滤。在它的具体对象上,可以调用setFilterIfMissing(true)或者setFilterIfMissing(false),默认的值是false,其作用是,对于咱们要使用作为条件的列,如果这一列本身就不存在,那么如果为true,这样的行将会被过滤掉,如果为false,这样的行会包含在结果集中。
1. SingleColumnValueFilter scvf = new SingleColumnValueFilter(
2. Bytes.toBytes("colfam1"),
3. Bytes.toBytes("qual2"),
4. CompareFilter.CompareOp.NOT_EQUAL,
5. new SubstringComparator("BOGUS"));
6. scvf.setFilterIfMissing(false);
7. scvf.setLatestVersionOnly(true); // OK
解决方案:右键项目----àbuildpath----à最下面那个configuration的选择libraries找到JRE(这个时候你会发现这个jre前面有!或者是红X)选中remove掉重新为该项目选择一个JRE选中项目,project----clean,也可能未添加JRE。
检查发现原来的包全部变成文件夹格式,我是右击src--àbuildpath--àuseas source folder就行了,就变回包格式的了,记得运行之后重启
在使用Java,double 进行运算时,经常出现精度丢失的问题,总是在一个正确的结果左右偏0.0000**1。特别在实际项目中,通过一个公式校验该值是否大于0,如果大于0我们会做一件事情,小于0我们又处理其他事情。 这样的情况通过double计算出来的结果去和0比较大小,尤其是有小数点的时候,经常会因为精度丢失而导致程序处理流程出错。
①进入kafka
cd kafka_2.10-0.10.0.0 (/home/hxxtsx/kafka_2.10-0.10.0.0)
②具体操作
bin/kafka-console-producer.sh --broker-listNM-304-SA5212M4-BIGDATA-659:9091 --topic systemlog --producer.configconfig/producer.properties
注:
bin/kafka-console-producer.sh (启动客户端)
--broker-listNM-304-SA5212M4-BIGDATA-659:9091 --topic systemlog --producer.configconfig/producer.properties (创建kafka消息和主题)
③设置json格式消息
{"system":"scheduler","module":"listener","host":"192.168.1.1","comm":"hello! this isalert!","logTime":"20171123102333","logType":"system","flag":"Exception"}
1. public class OneHour {
2. private final static long JOB_INTERNAL = 1000 * 60 * 60;//隔一个小时运行一次,如果是测试,可以改小一点
3. public static void main(String[] args) {
4. Timer timer = new Timer();
5. Calendar currentTime = Calendar.getInstance();
6. currentTime.setTime(new Date());
7.
8. int currentHour = currentTime.get(Calendar.HOUR);
9. currentTime.set(Calendar.HOUR, currentHour + 1);
10. currentTime.set(Calendar.MINUTE, 0);
11. currentTime.set(Calendar.SECOND, 0);
12. currentTime.set(Calendar.MILLISECOND, 0);
13. //下面几行是相隔时间短一点的测试代码
14. /*
15. currentTime.set(Calendar.HOUR, currentTime.get(Calendar.HOUR));
16. currentTime.set(Calendar.MINUTE, currentTime.get(Calendar.MINUTE));
17. currentTime.set(Calendar.SECOND, currentTime.get(Calendar.SECOND));
18. currentTime.set(Calendar.MILLISECOND, currentTime.get(Calendar.MILLISECOND));
19. */
20. Date NextHour = currentTime.getTime();
21. System.out.println(NextHour);
22. timer.scheduleAtFixedRate(new MyTask(), NextHour, JOB_INTERNAL);
23. }
24. }
①System.getProperty("user.dir")获取项目目录,配置文件和项目放在同一目录下
②allProperties.getProperty(“配置文件key”);获取配置参数
③System.getProperty("file.separator");路径分隔符
④KafkaConsumerconsumer = new KafkaConsumer<>(props); 通过配置文件创建kafka消费者
consumer.subscribe(Arrays.asList("topicName")); 消费者订阅主题
privatestatic Properties allProperties = new Properties();
publicstaticvoid main(String[] args) {
try {
InputStream in = new BufferedInputStream(newFileInputStream(System.getProperty("user.dir")+"/kafka_consumer_config.properties"));
allProperties.load(in);
} catch (Exception e) {
logger.error("读取Kafka配置文件出错!" + e);
}
}
//消费消息
while(true) {
ConsumerRecords
for (ConsumerRecordrecord: records) { // 消息内容record.value()
Map map =(Map) JSON.parse(record.value().toString());
System.out.println(i +"======"+map.toString());
// 写入文件
if(!TextUtil.isEmpty(msg)){
System.out.println("=====Prepare to write to the log file !");
writeFile(file, msg);
}else{
logger.error("日志消息为空,无法写入文件!");
}
}
}
/**
* 写入文件方法
* @param file
* @param msg
*/
publicstaticvoid writeFile(Filefile, String msg){
BufferedWriter bw = null;
// 文件不存在,创建文件
if(!file.exists()){
try {
System.out.println("file:"+file.getPath());
file.createNewFile();
} catch (IOException e) {
logger.error("文件" + file.getPath()+ "创建失败! " + e);
}
}
try {
// 以utf-8格式追加写
bw = new BufferedWriter(new OutputStreamWriter(newFileOutputStream(file, true),"UTF-8"));
bw.write(msg);
bw.flush();
bw.newLine(); //换行
System.out.println("===== 日志消息 " + msg + " 写入文件成功! " + file.getPath());
} catch (Exception e) {
logger.error("日志消息 " + msg + " 写入文件失败!" + e);
}finally{
try {
bw.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
eclipse中安装maven2插件之后,maven默认的本地库的路径是在${user}/.m2/repository/下,也就是在c盘的C:\Users\admin.m2\repository位置下。但是我们一般是不想放在C盘下,想将其放在别的盘符下,这里以D:\IDE\personalEclipse\repository为例:
1、手动创建本地仓库的地址为,在对应路径D:\IDE\personalEclipse\下创建repository文件夹,然后将D:\IDE\personalEclipse\apache-maven-3.3.9\conf\setting.xml这个路径下的文件复制到D:\IDE\personalEclipse\repository该文件夹下。
2、从windows的环境变量中增加了:M2_REPO=D:/IDE/personalEclipse/repository,同时把这个变量增加到path变量中。
3、修改D:\IDE\personalEclipse\apache-maven-3.3.9\conf\setting.xml文件,在localRepository标签中添加D:/IDE/personalEclipse/repository表示maven的本地库的路径
4、从eclipse->preferences->maven->installations下add一个已经安装到d盘的maven:
5、从eclipse->preferences->maven->installations下修改user setting 选项为:D:/IDE/personalEclipse/repository/settings.xml,接着修改global setting
D:\IDE\personalEclipse\apache-maven-3.3.9\conf\setting.xml,找到对应的文件路径即可
并点击updatesettings,然后点击下面的reindex按钮更新索引。
6、点击配置窗口的apply按钮即可
7、配置修改后,eclise会自动更新索引,当完成后重启myeclipse,会发现M2_REPO变量的值变成了D:/IDE/personalEclipse/repository。
/**
*"yyyyMMddHHmmss"字符串转成"yyyy-MM-dd HH:mm:ss"字符串
* @param str
* @return
*/
privatefinalstatic SimpleDateFormat sdfTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
privatefinalstatic SimpleDateFormat sdfTimes = new SimpleDateFormat("yyyyMMddHHmmss");
publicstatic String strToDateStr(String str){
Date date = null;
String dateStr = null;
try {
if(!TextUtil.isEmpty(str)){
date = sdfTimes.parse(str);
dateStr = sdfTime.format(date);
}
} catch (ParseException e) {
e.printStackTrace();
}
return dateStr;
}
①打开Xshell,属性à隧道(ssh里边)à添加
②在弹出的窗口填写要映射的机器IP及端口号,及映射到本地的IP及端口号,确认后连接即可通过映射的本地端口号访问目标机器
命令行运行grunt server
grunt server
Running
"connect:server"(
connect) task
Waiting forever...
Started
connectweb server on http:
//localhost:9100
打开http://localhost:9100进入管理页面,填写好地址点连接,然后连接不上elasticsearch ,浏览器错误详情提示不安全
Firefox Console:已拦截跨源请求:同源策略禁止读取位于 http://localhost:9200/ 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。
Chrome Console:XMLHttpRequest cannot load http://localhost:9200/. No'Access-Control-Allow-Origin' header is present on the requested resource.Origin 'http://localhost:9100' is therefore not allowed access.
因为端口不一样,所以浏览器自带的安全策略是不允许去请求不同源的资源。
修改Elasticsearch配置
elasticsearch.yml 添加下面配置,重启后即可生效
http.cors.enabled:
true
http.cors.allow-credentials:
true
http.cors.allow-methods: OPTIONS,HEAD,GET,POST,PUT,DELETE
http.cors.
max-age:
0
http.cors.allow-origin: /
http?:\/\/localhost(:[
0-9]+)?/
http.cors.allow-headers : X-Requested-With,X-Auth-Token,Content-Type,Content-Length
可以用 Nginx 做下反代, 然后加下Header。然后地址改为http://localhost:9201
server {
listen
9201
;
location~ /*
{
add_header
Access-Control-Allow-Origin *;
proxy_pass
http://127.0.0.1:9200;
}
}
之前也是碰到楼主上面的问题,跨域问题~ 修改elasticsearch 的 elasticsearch.yml 文件后解决,版本都是5.x 版本~
http.host:192.168.x.x (elasticsearch 的ip 一般 elasticsearch 和 head 是同一台上面~) http.cors.enabled: truehttp.cors.allow-origin: "*"
因为都是内网的IP所以还是http.cors.allow-origin:"*"
简单暴力好用
火狐和谷歌不支持访问,换IE后连接成功!
问题描述:es里有几个index,在概览下是可以看到的,但是在数据浏览页却什么都没有
解决方法:原来用的是火狐浏览器,换成chrome浏览器就好了
①修改注释颜色:window -->preferences --> java --> editor --> syntax coloring --> comments 中的前两个就是修改注释颜色的。点击右边的color修改颜色就行了。右侧还有Italy,Bold等字体样式,自己也可以选择。
②设置注释模板:window -->preferences --> java --> code style --> code Templates --> comments--> types --> edit
①遇到这个错误是因为你尝试对一个text类型的字段做排序,而text类型的字段是要分词的。一来词典很大,性能会很差;二来排序结果是词典里的词,而并非整个text的内容。出于这2点原因,ES5.x以后对于text类型默认禁用了fielddata,防止对text字段一些错误的操作(排序,聚合,script)而给heap造成很大的压力。
②ElasticSearch 5.x后对排序,聚合这些操作用单独的数据结构(fielddata)缓存到内存里了,需要单独开启,官方解释在此https://www.elastic.co/guide/en/elasticsearch/reference/current/fielddata.html
③简单来说就是在聚合前执行如下操作
PUT megacorp/_mapping/employee/
{
"properties": {
"interests": {
"type": "text",
"fielddata": true
}
}
}
④附java API说明文档 https://www.elastic.co/guide/e ... .html
⑤字段.keyword
26. 在使用eclipse构建maven项目时,突然出现错误提示:An error occurred while filtering resources,在项目中到处都找不到哪里有问题,最后在国外网站找到解决办法:
右键项目--》maven--》update project
用maven管理库依赖,有个好处就是连同库的依赖的全部jar文件一起下载,免去手工添加的麻烦,但同时也带来了同一个jar会被下载了不同版本的问题,好在pom的配置里面允许用
使用exclusion排除依赖
假设有这样一种依赖关系,A->B->C,这个时候由于某些原因,我们不需要对C的依赖,但是我们又必须要对B的依赖,这个时候该怎么办呢?针对这种情况,Maven给我们提供了一个exclusion功能,我们可以在添加A对B的依赖时申明不需要引进B对C的依赖。具体做法如下:
原因是因为JDK版本过低,ES仅支持JDK1.8及以上版本
为了简化读取properties文件中的配置值,spring支持@value注解的方式来获取,这种方式大大简化了项目配置,提高业务中的灵活性。
一、两种使用方法
1、@Value("#{configProperties['key']}")
2、@Value("${key}")
二、配置
2.1 @Value("#{configProperties['key']}")使用
2.1.1配置文件:
InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法。
测试程序如下:
ApplicationContext.xml配置文件如下:
Main主程序如下:
运行Main程序,打印如下结果:
这说明在spring初始化bean的时候,如果bean实现了InitializingBean接口,会自动调用afterPropertiesSet方法。
问题
实现InitializingBean接口与在配置文件中指定init-method有什么不同?
修改配置文件,加上init-method配置,修改如下:
在配置文件中加入init-method="testInit"。
运行Main程序,打印如下结果:
由结果可看出,在spring初始化bean的时候,如果该bean是实现了InitializingBean接口,并且同时在配置文件中指定了init-method,系统则是先调用afterPropertiesSet方法,然后在调用init-method中指定的方法。
这方式在spring中是怎么实现的?
通过查看spring的加载bean的源码类(AbstractAutowireCapableBeanFactory)可看出其中奥妙
AbstractAutowireCapableBeanFactory类中的invokeInitMethods讲解的非常清楚,源码如下:
总结
1:spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用
2:实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖
3:如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。
目前Java获取文件大小的方法有两种:
1、通过file的length()方法获取;
2、通过流式方法获取;
通过流式方法又有两种,分别是旧的java.io.*中FileInputStream的available()方法和新的java..nio.*中的FileChannel
下面依次介绍这几种方法:
一、通过length方法:
1、创建一个文件:
2、获取文件大小:
3、查看结果:可见,使用length方法获取的文件大小与windows中显示的大小一致!
二、通过file.io.*中的流式方法获取
1、创建一个文件
依旧使用上面的文件
2、使用available方法获取:
3、查看结果:通过这种方法获取的文件大小是2147483647,很明显,这是int类型所能表示的最大值2^31-1,究其原因是因为文件的大小超过了int所能表示的最大值!!!
file.length()方法返回的类型:
available()方法返回的类型:
三、通过file.nio.*中的FileChannel工具来获取文件大小:
1、创建一个文件
依旧使用相同的大文件:
2、使用FileChannel获取文件大小:
3、查看结果:
long count = new Double(8.9).longValue(); // 结果为8
昨晚安装另一个版本的eclipse,启动时出现了“Failed to load the JNI sharedlibrary jvm.dll”错误;
1、刚开始以为是因为当时没有将旧版本删除干净,于是就找了原来的安装目录,并没有发现残留的配置文件以及启动文件什么的;
2、于是猜测,新安装的这个版本目录下没有jvm.dll这个文件,可是发现是存在的;
路径1:C:\Program Files (x86)\Java\jre6\bin\client\jvm.dll(该路径下安装的是32位的Java jdk)
路径2:C:\Program Files\Java\jre6\bin\client\jvm.dll(该路径下安装的是64位的Java jdk)
3、后来网上查了一下,说是有可能是版本原因,于是用notepad++看了下eclipse的配置文件(位于eclipse目录下configuration文件夹中config.ini文件),发现新安装的eclipse是32位的,而我所用的jdk是64位的;(此为问题所在)
既然找到了问题,那就好办了,随即在自己的云盘里下了32位的jdk,并将环境变量中JAVA_HOME路径改为32位jdk的路径;32位的jdk和64位的jdk因为安装位置的不同,因此可以共存,需要用哪个,改一下JAVA_HOME路径就OK了。
正常在Java工程中读取某路径下的文件时,可以采用绝对路径和相对路径,绝对路径没什么好说的,相对路径,即相对于当前类的路径。在本地工程和服务器中读取文件的方式有所不同,以下图配置文件为例。
java类中需要读取properties中的配置文件,可以采用文件(File)方式进行读取:
1 File file = new File("src/main/resources/properties/basecom.properties");
2 InputStream in = new FileInputStream(file);
当在eclipse中运行(不部署到服务器上),可以读取到文件。
当工程部署到Tomcat中时,按照上边方式,则会出现找不到该文件路径的异常。经搜索资料知道,Java工程打包部署到Tomcat中时,properties的路径变到顶层(classes下),这是由Maven工程结构决定的。由Maven构建的web工程,主代码放在src/main/java路径下,资源放在src/main/resources路径下,当构建为war包的时候,会将主代码和资源文件放置classes文件夹下:
并且,此时读取文件需要采用流(stream)的方式读取,并通过JDK中Properties类加载,可以方便的获取到配置文件中的信息,如下:
1 InputStream in = this.getClass().getResourceAsStream("/properties/basecom.properties"); 2 Properties properties = new Properties(); 3 properties.load(in); 4 properties.getProperty("property_name");
其中properties前的斜杠,相对于调用类,共同的顶层路径。
如果工程中使用Spring,可以通过注解的方式获取配置信息,但需要将配置文件放到Spring配置文件中扫描后,才能将配置信息放入上下文。
1 <context:component-scan base-package="com.xxxx.service"/>
2 <context:property-placeholder location="classpath:properties/xxx.properties" ignore-unresolvable="true"/>
然后在程序中可以使用 @Value进行获取properties文件中的属性值,如下:
1 @Value("${xxxt.server}")
2 private static String serverUrl;
也可以在Spring配置文件中读取属性值,赋予类成员变量
1 xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans
5 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
6
7 <bean id=propertyConfigurer class=org.springframework.beans.factory.config.PropertyPlaceholderConfigurer>
8="location" value="classpath:properties/xxx.properties"/>
9 bean>
10
11 <bean id="service" class="com.xxxx.service.ServiceImpl">
12 <property name="serverUrl" value="${xxxt.server}">
13 bean>
14
15 beans>
Resourcefrom src/main/resources not found after building with maven
[Java] 在 jar 文件中读取resources 目录下的文件
根据Java规范:request.getParameterMap()返回的是一个Map类型的值,该返回值记录着前端(如jsp页面)所提交请求中的请求参数和请求参数值的映射关系。这个返回值有个特别之处——只能读。不像普通的Map类型数据一样可以修改。这是因为服务器为了实现一定的安全规范,所作的限制。比如WebLogic,Tomcat,Resin,JBoss等服务器均实现了此规范。
如果实在有必要在取得此值以后做修改的话,要新建一个map对象,将返回值复制到此新map对象中进行修改,用新的map对象代替使用之前的返回值。
1.如果字符串最后一位有值,则没有区别,
2.如果最后n位都是切割符,split(" ")不会继续切分,split(" ", -1)会继续切分
原因是由于JDK1.8新特性Lambda表达式 project Facets引用的是1.7版本,选中项目右键 --> Build Path --> Configure Build Path --> Maven -->project Facets --> java修改成1.8点击应用即可。
原因是没有启动集群模式(即缺少了那个"-c"):
启动命令:redis-cli -h10.142.119.9 -p 6379
修改为:redis-cli -c -h10.142.119.9 -p 6379
使用jedis插入数据时出现了一个问题:
1. redis.clients.jedis.exceptions.JedisDataException: WRONGTYPE Operation against a key holding the wrong kind of value
代码大概是这样实现的:
1. HashMap<String,String> map = new HashMap<String,String>();
2. map.put("mapKey", "20145");
3. jedis.hmset("key", map);
看起来没有值类型不对的样子。后来发现原来是因为redis数据库中已经存在了相同的key, 而且key对应的值类型并不是HashMap;再调用hmset时,就会抛出此错误。
把原来的数据清掉,重新运行就没问题了。
标准unix/linux下的grep通过下面參数控制上下文
grep-C 5 foo file 显示file文件里匹配foo字串那行以及上下5行
grep-B 5 foo file 显示foo及前5行
grep-A 5 foo file 显示foo及后5行
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
$command > /dev/null
/dev/null是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
如果希望屏蔽 stdout 和 stderr,可以这样写:
$command > /dev/null 2>&1
注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
java主线程等待所有子线程执行完毕在执行,这个需求其实我们在工作中经常会用到,比如用户下单一个产品,后台会做一系列的处理,为了提高效率,每个处理都可以用一个线程来执行,所有处理完成了之后才会返回给用户下单成功,下面就说一下我能想到的方法,欢迎大家批评指正:
1.用sleep方法,让主线程睡眠一段时间,当然这个睡眠时间是主观的时间,是我们自己定的,这个方法不推荐,但是在这里还是写一下,毕竟是解决方法
2.使用Thread的join()等待所有的子线程执行完毕,主线程在执行,thread.join()把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
下面结合这个问题我介绍一些并发包里非常有用的并发工具类
3.等待多线程完成的CountDownLatch
在这里说明一点,countDownLatch不可能重新初始化或者修改CountDownLatch对象内部计数器的值,一个线程调用countdown方法happen-before另外一个线程调用await方法
4.同步屏障CyclicBarrier
写到这里大家不免有些疑问,countDownLatch和cyclicBarrier有什么区别呢,他们的区别:countDownLatch只能使用一次,而CyclicBarrier方法可以使用reset()方法重置,所以CyclicBarrier方法可以能处理更为复杂的业务场景。
我曾经在网上看到一个关于countDownLatch和cyclicBarrier的形象比喻,就是在百米赛跑的比赛中若使用countDownLatch的话冲过终点线一个人就给评委发送一个人的成绩,10个人比赛发送10次,如果用CyclicBarrier,则只在最后一个人冲过终点线的时候发送所有人的数据,仅仅发送一次,这就是区别。
JDk1.5提供了一个非常有用的包,Concurrent包,这个包主要用来操作一些并发操作,提供一些并发类,可以方便在项目当中傻瓜式应用。
JDK1.5以前,使用并发操作,都是通过Thread,Runnable来操作多线程;但是在JDK1.5之后,提供了非常方便的线程池(ThreadExecutorPool),主要代码由大牛Doug Lea完成,其实是在jdk1.4时代,由于java语言内置对多线程编程的支持比较基础和有限,所以他写了这个,因为实在太过于优秀,所以被加入到jdk之中;
这次主要对CountDownLatch进行系统的讲解
使用场景:比如对于马拉松比赛,进行排名计算,参赛者的排名,肯定是跑完比赛之后,进行计算得出的,翻译成Java识别的预发,就是N个线程执行操作,主线程等到N个子线程执行完毕之后,在继续往下执行。
代码示例
1 public static void testCountDownLatch(){
3 int threadCount = 10;
5 final CountDownLatch latch = new CountDownLatch(threadCount);
7 for(int i=0; i< threadCount; i++){
9 new Thread(new Runnable() {
11 @Override
12 public void run() {
14 System.out.println("线程" + Thread.currentThread().getId() + "开始出发");
16 try {
17 Thread.sleep(1000);
18 } catch (InterruptedException e) {
19 e.printStackTrace();
20 }
22 System.out.println("线程" + Thread.currentThread().getId() + "已到达终点");
24 latch.countDown();
25 }
26 }).start();
27 }
29 try {
30 latch.await();
31 } catch (InterruptedException e) {
32 e.printStackTrace();
33 }
35 System.out.println("10个线程已经执行完毕!开始计算排名");
36 }
执行结果:
线程10开始出发
线程13开始出发
线程12开始出发
线程11开始出发
线程14开始出发
线程15开始出发
线程16开始出发
线程17开始出发
线程18开始出发
线程19开始出发
线程14已到达终点
线程15已到达终点
线程13已到达终点
线程12已到达终点
线程10已到达终点
线程11已到达终点
线程16已到达终点
线程17已到达终点
线程18已到达终点
线程19已到达终点
10个线程已经执行完毕!开始计算排名
源码分析:
1、CountDownLatch:Asynchronization aid that allows one or more threads to wait until a set ofoperations being performed in other threads completes.
大致意思:也就是说主线程在等待所有其它的子线程完成后再往下执行
2、构造函数:CountDownLatch(int count)//初始化count数目的同步计数器,只有当同步计数器为0,主线程才会向下执行
主要方法:void await()//当前线程等待计数器为0
boolean await(long timeout, TimeUnit unit)//与上面的方法不同,它加了一个时间限制。
void countDown()//计数器减1
long getCount()//获取计数器的值
3.它的内部有一个辅助的内部类:sync.
它的实现如下:
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 |
|
4.await()方法的实现
sync.acquireSharedInterruptibly(1);
-->if (tryAcquireShared(arg) < 0)//调用3中的tryAcquireShared()方法
doAcquireSharedInterruptibly(arg);//加入到等待队列中
5.countDown()方法的实现
sync.releaseShared(1);
--> if (tryReleaseShared(arg))//调用3中的tryReleaseShared()方法
doReleaseShared();//解锁