tonglin0325的个人主页

Java多线程——可阻塞的队列BlockingQueue

阻塞队列Semaphore有些相似,但也不同,阻塞队列一方存放数据,另一方释放数据,Semaphore通常则是由同一方设置和释放信号量。

ArrayBlockingQueue

  只有put方法和take方法才具有阻塞功能

用3个空间的队列来演示阻塞队列的功能和效果。

用两个具有1个空间的队列来实现同步通知的功能。

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
package java_thread;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class BlockingQueueTest {
public static void main(String[] args) {
final BlockingQueue queue = new ArrayBlockingQueue(3);
for(int i=0;i<2;i++){ //2个线程放数据
new Thread(){
public void run(){
while(true){
try {
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName() + "准备放数据!");
queue.put(1); //如果队列满的话,将在这里阻塞
System.out.println(Thread.currentThread().getName() + "已经放了数据," +
"队列目前有" + queue.size() + "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
}

}
}

}.start();
}

new Thread(){ //1个线程取数据
public void run(){
while(true){
try {
//将此处的睡眠时间分别改为100和1000,观察运行结果
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "准备取数据!");
queue.take(); //如果队列空的话,将在这里阻塞
System.out.println(Thread.currentThread().getName() + "已经取走数据," +
"队列目前有" + queue.size() + "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}.start();
}
}

 

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
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package sort;

public class Heap_Sort {

public static void main(String[] args) {
// TODO 自动生成的方法存根
Heap_Sort qs = new Heap_Sort();
int[] Arr = {10,9,8,7,6,5,4,3,2,1};
qs.heapSort(Arr);
for(int i=0;i<Arr.length;i++){
System.out.println(Arr[i]);
}
}

//调整函数
public void headAdjust(int[] elements,int pos,int len){
//将当前节点值进行保存
int swap = elements[pos];

//定位到当前节点的左边的子节点
int child = pos * 2 + 1;

//递归,直至没有子节点为止
while(child < len){
//如果当前节点有右边的子节点,并且右子节点较大的场合,采用右子节点和当前节点进行比较
if(child + 1 < len &amp;&amp; elements[child] < elements[child + 1]){
child += 1;
}

//比较当前节点和最大的子节点,小于则进行值交换,交换后将当前节点定位于子节点上
if(elements[pos] < elements[child]){
elements[pos] = elements[child];
pos = child;
child = pos * 2 + 1;
}
else{
break;
}

elements[pos] = swap;
}
}

//构建堆
public void buildHeap(int[] elements){
//从最后一个拥有子节点的节点开始,将该节点连同其子节点进行比较,
//将最大的数交换与该节点,交换后,再依次向前节点进行相同交换处理,
//直至构建出大顶堆(升序为大顶,降序为小顶)
for(int i=elements.length/2; i>=0; i--){
headAdjust(elements, i, elements.length);
}
}

public void heapSort(int[] elements){
//构建堆
buildHeap(elements);

//从数列的尾部开始进行调整
for(int i=elements.length-1; i>0; i--){
//堆顶永远是最大元素,故,将堆顶和尾部元素交换,将
//最大元素保存于尾部,并且不参与后面的调整
//alert(elements);
int swap = elements[i];
elements[i] = elements[0];
elements[0] = swap;
//alert(elements);
//进行调整,将最大的元素调整至堆顶
headAdjust(elements, 0, i);
//alert(elements);
}
}

}

 

Java多线程——Executors和线程池

线程池的概念与Executors类的应用

**  1.创建固定大小的线程池**

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
package java_thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadPoolTest {

/**
* @param args
*/
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
// ExecutorService threadPool = Executors.newCachedThreadPool();
// ExecutorService threadPool = Executors.newSingleThreadExecutor();
for(int i=1;i<=10;i++){
final int task = i;
threadPool.execute(new Runnable(){
@Override
public void run() {
for(int j=1;j<=4;j++){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is looping of " + j + " for task of " + task);
}
}
});
}
System.out.println("all of 10 tasks have committed! ");
//threadPool.shutdownNow();

// Executors.newScheduledThreadPool(3).scheduleAtFixedRate(
// new Runnable(){
// @Override
// public void run() {
// System.out.println("bombing!");
//
// }},
// 6,
// 2,
// TimeUnit.SECONDS);
}

}

**  2.创建缓存线程池**

1
2
ExecutorService threadPool = Executors.newCachedThreadPool();

** 3.创建单一线程池**

1
2
ExecutorService threadPool = Executors.newSingleThreadExecutor();

 

 

**关闭线程池 **

  shutdown与shutdownNow的比较

1
2
threadPool.shutdownNow();

 

全文 >>

Java多线程——线程范围内共享变量和ThreadLocal

多个线程访问共享对象和数据的方式

  1.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。

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
package java_thread;

class MyThread_2 implements Runnable{
private int ticket = 5;

@Override
public void run() { //覆写Thread类中的run()方法
// TODO 自动生成的方法存根
for (int i=0;i<10;i++){
synchronized (this) { //设置需要同步的操作
if(ticket>0){
try{
Thread.sleep(300);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("卖票:ticket="+ticket--);
}
}
// this.sale(); //调用同步方法
}
}

// public synchronized void sale(){ //声明同步方法
// if(ticket>0){
// try{
// Thread.sleep(300);
// }catch(InterruptedException e){
// e.printStackTrace();
// }
// System.out.println("卖票:ticket="+ticket--);
// }
// }

}

public class Runnable_demo2 {

public static void main(String[] args) {
// TODO 自动生成的方法存根
MyThread_2 mt = new MyThread_2(); //实例化Runnable子类对象
Thread t1 = new Thread(mt); //实例化Thread类对象
Thread t2 = new Thread(mt); //实例化Thread类对象
Thread t3 = new Thread(mt); //实例化Thread类对象
t1.start(); //启动线程
t2.start(); //启动线程
t3.start(); //启动线程
}

}

 

  2.如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:

    方法1:将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。

 

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package java_thread;

public class MultiThreadShareData { //多线程卖票,一个加,一个减

//private static ShareData1 data1 = new ShareData1();

public static void main(String[] args) {
ShareData1 data2 = new ShareData1();
new Thread(new MyRunnable1(data2)).start();
new Thread(new MyRunnable2(data2)).start();

final ShareData1 data1 = new ShareData1();
new Thread(new Runnable(){
@Override
public void run() {
data1.decrement();

}
}).start();
new Thread(new Runnable(){
@Override
public void run() {
data1.increment();

}
}).start();

}

}

class MyRunnable1 implements Runnable{ //线程1
private ShareData1 data1;
public MyRunnable1(ShareData1 data1){
this.data1 = data1;
}
public void run() {
data1.decrement();

}
}

class MyRunnable2 implements Runnable{ //线程2
private ShareData1 data1;
public MyRunnable2(ShareData1 data1){
this.data1 = data1;
}
public void run() {
data1.increment();
}
}

class ShareData1 /*implements Runnable*/{  //共享对象
/* private int count = 100;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
count--;
}
}*/

private int j = 0;
public synchronized void increment(){
j++;
}

public synchronized void decrement(){
j--;
}
}

 

    方法2:将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。

 

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
package java_thread;

//设计 4 个线程,其中两个线程每次对 j 增加 1,另外两个线程对 j 每次减少 1
public class ThreadTest1{

private int j;
public static void main(String args[]){
ThreadTest1 tt=new ThreadTest1();
Inc inc=tt.new Inc();
Dec dec=tt.new Dec();
for(int i=0;i<2;i++){
Thread t=new Thread(inc);
t.start();
t=new Thread(dec);
t.start();
}
}

private synchronized void inc(){
j++;
System.out.println(Thread.currentThread().getName()+"-inc:"+j);
}

private synchronized void dec(){
j--;
System.out.println(Thread.currentThread().getName()+"-dec:"+j);
}

class Inc implements Runnable{ //线程1
public void run(){
for(int i=0;i<5;i++){
inc();
}
}
}

class Dec implements Runnable{ //线程2
public void run(){
for(int i=0;i<5;i++){
dec();
}
}
}

}

 

    上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。

    总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。

全文 >>

Spring MVC学习笔记——SiteMesh的使用(转)

转自 SiteMesh的使用

 

SiteMesh的介绍就不多说了,主要是用来统一页面风格,减少重复编码的。

它定义了一个过滤器,然后把页面都加上统一的头部和底部。

需要先在WEB-INF/lib下引入sitemesh的jar包:http://wiki.sitemesh.org/display/sitemesh/Download 。这里使用2.4版本。

 

过滤器定义:

在web.xml中

1
2
3
4
5
6
7
8
9
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

 

decorators.xml文件:

WEB-INF下新建decorators.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<decorators defaultdir="/WEB-INF/layouts/">
<!-- 此处用来定义不需要过滤的页面 -->
<excludes>
<pattern>/static/*</pattern>
</excludes>

<!-- 用来定义装饰器要过滤的页面 -->
<decorator name="default" page="default.jsp">
<pattern>/*</pattern>
</decorator>
</decorators>

全文 >>

clickhouse学习笔记——Go客户端连接clickhouse

1.创建clickhouse环境

安装clickhouse

参考:ubuntu16.04安装clickhouse

或者使用docker

参考:https://hub.docker.com/r/clickhouse/clickhouse-server

1
2
docker run -d -p 18123:8123 -p 19000:9000 --name some-clickhouse-server --ulimit nofile=262144:262144 clickhouse/clickhouse-server:23.8

使用datagrip连接

创建表和测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE TABLE default.my_first_table
(
user_id UInt32,
message String,
timestamp DateTime,
metric Float32
)
ENGINE = MergeTree()
PRIMARY KEY (user_id, timestamp);

INSERT INTO default.my_first_table (user_id, message, timestamp, metric) VALUES
(101, 'Hello, ClickHouse!', now(), -1.0 ),
(102, 'Insert a lot of rows per batch', yesterday(), 1.41421 ),
(102, 'Sort your data based on your commonly-used queries', today(), 2.718 ),
(101, 'Granules are the smallest chunks of data read', now() + 5, 3.14159 )

2.使用client连接clickhouse

golang客户端连接clickhouse,可以使用 clickhouse-go 这个库

全文 >>

Amazon S3限流机制

当使用S3作为Amazon EMR的存储的时候,当写入的流量比较大的时候,有时会遇到性能瓶颈,报错如下

1
2
Caused by: com.amazonaws.services.s3.model.AmazonS3Exception: Please reduce your request rate.

在如下的AWS文章中介绍,S3的性能不是按照bucket定义的,而是按照bucket的prefix,对于每个prefix,3500的PUT/COPY/POST/DELETE能力和5000的GET/HEAD能力,如果超过这个限制的话,就会被限流

关于prefix定义,参考文档:aws s3原理和常用命令

参考:从 Amazon EMR 和 AWS Glue 访问 Amazon S3 中数据的性能优化最佳实践

在咨询了AWS的工程师后,说上面的文档不是很准确,更具体的回答是:

1.对于一个刚创建的bucket,默认每个bucket有3500写+5000读的能力

2.当S3 bucket的流量增加后,S3会对应进行bucket的split partition,根据的规则是按照prefix逐位向后分割

意思是对于S3://bucket_name/[a-z,0-9][a-z,0-9]…./object的s3存储结构,prefix的第1个字符有a-z+0-9(26+10=36)种可能性,如果这36个字符出现的可能性是一样的话,则当整个bucket的流量很大的时候,bucket就会扩容到36个partition,从而获得36*(3500/5000)的能力

全文 >>

Spring MVC学习笔记——文件上传

1.实现文件上传首先需要导入Apache的包,commons-fileupload-1.2.2.jar和commons-io-2.1.jar

  实现上传就在add.jsp文件中修改表单

1
2
3
4
5
6
enctype="multipart/form-data"

<tr>
  |附件:|<input type="file" name="attach"/>
</tr>

 完整的add.jsp文件

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
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<sf:form method="post" modelAttribute="user" enctype="multipart/form-data">
<table width="700" align="center" border="1">
<tr>
|用户名:|<sf:input path="username"/><sf:errors path="username"/>
</tr>
<tr>
|用户密码:|<sf:password path="password"/><sf:errors path="password"/>
</tr>
<tr>
|用户昵称:|<sf:input path="nickname"/>
</tr>
<tr>
|用户邮箱:|<sf:input path="email"/><sf:errors path="email"/>
</tr>
<tr>
|附件:|<input type="file" name="attach"/>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="用户添加"/>
</td>
</tr>
</table>
</sf:form>

</body>
</html>

 

2.在user-servlet.xml中配置上传文件

1
2
3
4
5
<!-- 配置上传文件CommonsMultipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="5000000"></property>
</bean>

 

3.在控制器中修改add()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//在具体添加用户的时候,是POST请求,就访问以下代码
@RequestMapping(value="/add",method=RequestMethod.POST)
public String add(@Validated User user,BindingResult br,MultipartFile attach,HttpServletRequest req) throws IOException{//一定要紧跟@Validated之后写验证结果类
if(br.hasErrors()){
//如果有错误,直接跳转到add视图
return "user/add";
}
String realpath = req.getSession().getServletContext().getRealPath("/resources/upload"); //取得会话对象的路径
System.out.println(realpath);
File f = new File(realpath+"/"+attach.getOriginalFilename());
FileUtils.copyInputStreamToFile(attach.getInputStream(), f);
System.out.println(attach.getName()+","+attach.getOriginalFilename()+","+attach.getContentType());
users.put(user.getUsername(),user); //把key和user对象放进Map中
return "redirect:/user/users";
}

 

 还需要在resources文件夹下面添加upload文件夹

全文 >>

Spring MVC学习笔记——完整的用户登录

1.搭建环境的第一步是导包,把下面这些包都导入工程中

/media/common/工作/Ubuntu软件/SpringMVC_jar包整理/aop
/media/common/工作/Ubuntu软件/SpringMVC_jar包整理/apache-commons-logging
/media/common/工作/Ubuntu软件/SpringMVC_jar包整理/apache-log4j
/media/common/工作/Ubuntu软件/SpringMVC_jar包整理/bean-validator
/media/common/工作/Ubuntu软件/SpringMVC_jar包整理/dbcp
/media/common/工作/Ubuntu软件/SpringMVC_jar包整理/hibernate-3.6.8.
/media/common/工作/Ubuntu软件/SpringMVC_jar包整理/JSTL
/media/common/工作/Ubuntu软件/SpringMVC_jar包整理/mysql
/media/common/工作/Ubuntu软件/SpringMVC_jar包整理/pager
/media/common/工作/Ubuntu软件/SpringMVC_jar包整理/sitemesh
/media/common/工作/Ubuntu软件/SpringMVC_jar包整理/spring

手动导包也可以,不过不是很方便,推荐学习使用maven的pom.xml文件来导入jar包

整个系统的结构

表示层(JSP页面),一般包名是view

**    ▼**

控制层,一般包名是action或者web,控制层也会操作实体层

**  ▼**

业务逻辑层,一般包名是service

**  ▼**

数据持久层,一般包名是dao

**  ▼**

实体层(JavaBean),一般包名是model或者entity

 

写成的过程和上面的方向相反,从下往上写

实体Entity层

**  1.先写User类**

**    Id,username,nickname,password,email**

**    其中还包括注入**

**  2.再写Page类**

**    public class Pager**

**    List datas、offset、size、total**

**  3.写SystemContext类**

**  7.写UserException异常类**

 

数据持久层dao层,主要是操作Hibernate,还要写beans.xml

**  4.写IUserDao接口**

**    增、更新、删除、根据ID查用户load、查所用用户List list、查分页Pager find、根据username查用户loadByUsername**

**  5.实现IUserDao接口**

**    分页find()中取得SystemContext类**

 

业务逻辑层service层,主要是写验证

**  6.写IUserService接口**

**    增、更新、删除、根据ID查用户load、查所用用户List list、查分页Pager find、根据username查用户loadByUsername**

**  8.实现IUserService接口**

    密码登录验证login、添加用户、修改用户、删除用户、查询用户、列出所有用户、分页find()

 

控制层action层

  9.LoginFilter.java登录权限,实现Filter接口,doFilter()方法

**    在请求是/user/的时候拦截验证权限,没有权限重定向/login,有权限放行*

  10.SystemContext.java分页过滤,实现Filter接口,doFilter()方法

 

**    在请求是/中,如果参数为Pager.offset的时候,拦截取得offset,设置SystemContext中的offset和size*

  11.IndexController.java,Session共享数据

    在请求是/login的时候,将ModelMap中的属性放入Session中,实现多窗口共享数据

  12.UserController.java,总的请求为/user,这也就是MVC模型中的RequestMapping

    在请求是/user和/的时候,向model模型中添加——userService.find()

    在请求是/add的时候(分GET和POST),向model模型中添加——new User()

    在请求是/{id}的时候,向model模型中添加——userService.load(id)

    在请求是/{id}/update的时候**(分GET和POST)**

    在请求是/{id}/delete的时候…

 

    最后再传给DispatchServlet,使用model从Controller给视图传值

    在jsp中通过 ${ } 取得属性

    记得加上@Controller,通过Annotation来配置控制器

 

注意:在持久层、业务层、控制层中,分别采用@Repository、@Service、@Controller对分层中的类进行注释

 

全文 >>

Spring MVC学习笔记——引入静态文件

1.在user-servlet.xml中加入以下代码,才能使得对静态文件的请求不被Controller捕获,而映射到一个固定的地址

1
2
3
<!-- 将静态文件指定到某个特殊的文件夹中统一处理 -->
<mvc:resources location="/resources/" mapping="/resources/**"></mvc:resources>

 

2.在WebContent文件下面,添加resources文件夹和css/main.css文件

**  mian.css文件,文字的大小和颜色**

1
2
3
4
5
*{
font-size:14px;
color:#f00;
}

 

3.在list.jsp文件中,加入css样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!-- 加上标签库 -->
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>用户列表</title>
<link rel="stylesheet" href="<%=request.getContextPath()%>/resources/css/main.css" type="text/css">
</head>
<body>
[Add](add)-->${loginUser.nickname }--${tttt }<br/>
<c:forEach items="${users }" var="um">
[${um.value.username }](${um.value.username })--------
${um.value.nickname }-------
${um.value.password }----${um.value.email }
[更新](${um.value.username }/update)
[删除](${um.value.username }/delete)
<br/>
</c:forEach>
</body>
</html>