0%

文章字数:442,阅读全文大约需要1分钟
2^n|10进制值|2进制
—|—|—:
1|2|10
2|4|100
3|8|1000
4|16|10000
5|32|100000
6|64|1000000
7|128|10000000
8|256|100000000
9|512|1000000000
10|1024|10000000000
11|2048|100000000000
12|4096|1000000000000
13|8192|10000000000000
14|16384|100000000000000
15|32768|1000000000000000
16|65536|10000000000000000
17|131072|100000000000000000
18|262144|1000000000000000000
19|524288|10000000000000000000
20|1048576|100000000000000000000

生成算法

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
final int MAX_POW = 20;
System.out.println("2^n|10进制值|2进制");
System.out.println("---|---|---:");
for (int i = 1; i <= MAX_POW; i++) {
int radix10 = Double.valueOf(Math.pow(2, i)).intValue();
System.out.print(i + "|" + radix10);
System.out.println("|`" + Integer.toString(radix10, 2) + "`");
}
}

进制转换

Integer类提供了方法,可以把任意进制的数转换成Integer类型的值。也可以将Integer输出成任意进制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
int initVal = 12354;
// 输出
String radix2 = Integer.toString(initVal, 2);
String radix10 = Integer.toString(initVal, 10);
String radix16 = Integer.toString(initVal, 16);
// 输入
Integer fromRadix2 = Integer.valueOf(radix2, 2);
Integer fromRadix10 = Integer.valueOf(radix10, 10);
Integer fromRadix16 = Integer.valueOf(radix16, 16);
// 结果
System.out.println("radix2 = " + radix2);
System.out.println("radix10 = " + radix10);
System.out.println("radix16 = " + radix16);
System.out.println("fromRadix2 = " + fromRadix2);
System.out.println("fromRadix10 = " + fromRadix10);
System.out.println("fromRadix16 = " + fromRadix16);
}

运行结果

1
2
3
4
5
6
radix2 = 11000001000010
radix10 = 12354
radix16 = 3042
fromRadix2 = 12354
fromRadix10 = 12354
fromRadix16 = 12354

文章字数:112,阅读全文大约需要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
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package org.colin;

import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
* @author colin.cheng
* @date 2021-10-09 16:26
* @since 1.0.0
*/
public class HostSearchProcessor {

static final Pattern titlePattern = Pattern.compile("<title>.*</title>");

/**
* 尝试用socket连接某个url的端口
*
* @param url
* @param port
* @return
*/
public static boolean tryByUrl(String url, int port) {
try {
final Socket socket = new Socket(url, port);
socket.close();
return true;
} catch (Exception e) {
return false;
}
}

/**
* ping 某个地址
*
* @param ipAddress
* @return
*/
public static boolean ping(String ipAddress) {
int timeOut = 3000;
boolean status;
try {
status = InetAddress.getByName(ipAddress).isReachable(timeOut);
} catch (IOException e) {
return false;
}
return status;
}

/**
* 批量构造地址
*
* @param networkSegment 网段
* @param start 开始地址
* @param end 结束地址
* @return
*/
public static List<String> getHostByNetworkSegment(String networkSegment, int start, int end) {
return IntStream.range(start, end).mapToObj((i) -> networkSegment + "." + i).collect(Collectors.toList());
}

/**
* 输出某个范围内地址指定端口的服务名
*
* @param networkSegment 网段
* @param start 网段开始
* @param end 网段结束
* @param port 指定端口
* @throws KeyManagementException
* @throws NoSuchAlgorithmException
*/
public static void consoleServerTitleByNetworkSegment(String networkSegment, int start, int end, int port) throws KeyManagementException, NoSuchAlgorithmException {
final CloseableHttpClient httpClient = HttpClientUtil.getHttpClientBuilder().build();
final CloseableHttpClient httpsClient = HttpClientUtil.getHttpsClientBuilder().build();

RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(3000)
.setConnectionRequestTimeout(1000)
.setSocketTimeout(3000).build();

final List<String> hostList = getHostByNetworkSegment(networkSegment, start, end);
for (String host : hostList) {
boolean isReachable = ping(host);
System.out.println("ping " + host + ", res = " + isReachable);
if(isReachable) {
// http
try {
String url = "http://" + host + ":" + port;
searchServerByUrl(httpClient, requestConfig, url);
} catch (Exception e) {}
// https
try {
String url = "https://" + host + ":" + port;
searchServerByUrl(httpsClient, requestConfig, url);
} catch (Exception e) {}
}
}
}

private static void searchServerByUrl(CloseableHttpClient httpClient, RequestConfig requestConfig, String url) throws IOException {
HttpGet httpGet = HttpClientUtil.createHttpGet(url, null);
httpGet.setConfig(requestConfig);
final String res = HttpClientUtil.parseRespToStr(httpClient.execute(httpGet));
final Matcher matcher = titlePattern.matcher(res);
if(matcher.find()) {
String title = matcher.group(0);
title = title.replace("<title>", "");
title = title.replace("</title>", "");
System.out.println(url + " server title is " + title);
}
}
}

文章字数:437,阅读全文大约需要1分钟

互斥锁是最普遍的多线程锁,并发编程除了考虑运行结果的正确之外,还需要线程无死锁(线程永久停滞),无饥饿(部分线程永远拿不到执行权限)

特性

  1. 互斥: 保证互斥行的专有名词是临界区,临界区内的代码在某个时刻只能被一个线程执行。即被锁保护的代码区域。
  2. 无死锁: 不会永久停滞
  3. 无饥饿: 不会部分永久停滞
1
2
3
4
5
6
7
/**
* 一个锁的基本形态
**/
interface Lock {
public void lock();//锁定
public void unlock();//解锁
}

注意事项

  1. 使用方式
1
2
3
4
5
6
7
//在try之前就需要获取锁,避免发生异常时未获取到锁就执行解锁。
mutex.lock();
try{
...临界区
}finally{
mutex.unlock()
}
  1. 减少互斥锁粒度: 锁粒度越小,程序执行是需要串行执行的工作就会越少。就可以更充分利用多处理器并行处理的优势。

  2. 进来不用锁: 减少串行执行工作量。

互斥锁示例

《多处理器编程的艺术》中Peterson算法,保证两个线程使用锁时互斥,无死锁,无饥饿。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

class Peterson implements Lock {
private boolean[] flag = new boolean[2];
private int victim;
public void lock(){
int i = ThreadID.get();
int j = 1 - i; //因为只有两个,所以1-i就是另一个线程的id
flag[i]= true; // 标记当前线程需要获取锁
victim = i; // 并发时后改变这个值的线程自旋(保证早获取的执行,防止饥饿)
while(flag[j] && victim == i){} // 当另一个锁需要执行,自己又获取的晚,那么等待另一个执行完。
}

public void unlock(){
int i = ThreadID.get();
flag[i] = false;
}

}

文章字数:253,阅读全文大约需要1分钟

1byte = 8bit
内部只有0和1
每一位用来存储某种状态,适合大数据,但是数据状态并不多的情况。

优缺点

优点:占用空间小,只保存状态
缺点:只能保存状态,且只有0和1。不能计算个数(天然去重)

使用

  1. 上亿级别的ip黑名单,可以使用Hash算出黑名单处于map的位置并且设置值为1。

布隆过滤器

  1. 使用三次Hash在位图中存储的方式表示元素是否存在于过滤器中
  2. 使用三次Hash命中不同位置(尽量避免hash碰撞),三次都未命中则一定不存在。并不能保证判断存在。
  3. 范围越大误判率越低,初始化大小决定了过滤器的误判率。
  4. 删除很困难。
    google.guava这个包里有
    redis中有布隆过滤器的插件,可以直接用bf.addbf.exists
    bf.reserve xx 0.00001设置误判率

文章字数:456,阅读全文大约需要1分钟

看源码时经常能看到一些位运算的操作,这里列举几种常见的用法。

乘除

  1. 2^n

    1
    2
    3
    4
    5
    6
    7
    public static void main(String[] args) {
    int val = 461637107;
    int res1 = val / 16;
    int res2 = val >> 4;
    System.out.println("res1 = " + res1);// res1 = 28852319
    System.out.println("res2 = " + res2);// res2 = 28852319
    }
  2. 2^n

    1
    2
    3
    4
    5
    6
    7
    public static void main(String[] args) {
    int val = 134563;
    int res1 = val * 16;
    int res2 = val << 4;
    System.out.println("res1 = " + res1);// res1 = 2153008
    System.out.println("res2 = " + res2);// res2 = 2153008
    }

取模

1
2
3
4
5
6
7
public static void main(String[] args) {
int val = 461637107;
int res1 = val % 32;
int res2 = val & 0x1F;// 31的16进制,即5个1。
System.out.println("res1 = " + res1);//res1 = 19
System.out.println("res2 = " + res2);//es2 = 19
}

设置具体bit位的数据为1

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
DecimalFormat df = new DecimalFormat("00000000");
int oldVal = 5;
int n = 3;
int newVal = oldVal | 1 << n;
// 得到的数据
System.out.println(oldVal); // 5
// 输出二进制
System.out.println(df.format(Integer.valueOf(Integer.toString(oldVal, 2))));//00000101
System.out.println(df.format(Integer.valueOf(Integer.toString(newVal, 2))));//00001101
}

设为0同样思路

获取二进制第n个位置上的值

1
2
3
4
5
6
7
public static void main(String[] args) {
int n = 0;
String val = "00001101";
Integer valInt = Integer.valueOf(val, 2);
boolean res = (valInt & 1 << n) != 0;
System.out.println(res ? "1" : "0");//1
}

基本运算符

1.&(按位与运算) 只要有一个为0,就为0,如 0001 & 0100 结果为 0000
2. |(按位或运算) 只要有一个为1,就为1,如 0001 & 0100 结果为 0101
3. ~(取反运算) 这个只对一个数据进行操作,0取反为1,1取反为0;
4. ^ (按位异或运算) 不同为1,相同为0, 如 0001 ^ 0100 结果为 0101


文章字数:159,阅读全文大约需要1分钟

  1. servlet
1
2
3
4
5
6
7
8
9
10
11
public class HelloServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("hellowrld");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
  1. 创建内置的tomcat并使用servlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 创建tomcat应用对象
Tomcat tomcat = new Tomcat();
// 设置端口
tomcat.setPort(8080);
// 是否自动部署
tomcat.getHost().setAutoDeploy(false);
// 创建上下文
StandardContext standardContext = new StandardContext();
// 设置项目名
standardContext.setPath("/sb");
// 监听上下文
standardContext.addLifecycleListener(new FixContextListener());
// 向tomcat容器对象添加上下文配置 tomcat.getHost().addChild(standardContext);
// 创建Servlet
tomcat.addServlet("/sb", "helloword", new HelloServlet());
// Servlet映射
standardContext.addServletMappingDecoded("/hello", "helloword");
//启动tomcat容器
tomcat.start();
//等待
tomcat.getServer().await();

文章字数:531,阅读全文大约需要2分钟

解释

  1. 元空间是jdk1.8之后出现的,之前叫永久代
  2. 元空间就是方法区,方法区是JVM的规范,元空间是HotSpot的具体实现
  3. 元空间存放着类的信息、方法数据、方法代码、常量池等
  4. 编译的时候代码里的字符串就会被编译成对象,然后被堆里的引用对象引用。

相关参数

  • XX:MetaspaceSize 初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
  • XX:MaxMetaspaceSize最大空间,默认是没有限制的。

除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:

  • XX:MinMetaspaceFreeRatioGC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
  • XX:MaxMetaspaceFreeRatioGC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

文章字数:213,阅读全文大约需要1分钟

冒泡排序的思想就是将所有的元素和边上其它元素比较,按照比对结果替换位置。(如果比后一个元素大就替换位置,继续和下一个比)本质上是大的向后移动,小的向前移动。

时间复杂度 o(n^2)

实现

1
2
3
4
5
6
7
8
9
10
11
12
public static void sort(int arr[]){
for( int i = 0 ; i < arr.length - 1 ; i++ ){
for(int j = 0;j < arr.length - 1 - i ; j++){
int temp = 0;
if(arr[j] < arr[j + 1]){
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}

优化

如果已经是顺序数组,就不要进行后面的遍历了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void sort(int arr[]){
for( int i = 0;i < arr.length - 1 ; i++ ){
boolean isSort = true;
for( int j = 0;j < arr.length - 1 - i ; j++ ){
int temp = 0;
if(arr[j] < arr[j + 1]){
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
isSort = false;
}
}
if(isSort){
break;
}
}
}

文章字数:137,阅读全文大约需要1分钟

标准

  • 并发数TPS:每秒处理的事务(完整请求)TomCat保守一般为500
  • 数据量 mysql单表数据量不超过700w

缓存

  • 一般方案
  1. 查缓存,有值直接返回
  2. 无值查数据库(缓存穿透、击穿),补入缓存
  3. 缓存和数据库数据不一致,通过过期时间调节
  4. 缓存区在某一时间一起失效,将大量缓存击穿(雪崩)
  • 永不过期
  1. 不设置过期时间
  2. 额外同步机制

文章字数:1203,阅读全文大约需要4分钟

分布式事务

  1. 是什么
  • 分布式事务就是一次大的事务操作由不同的小操作组成,这些小操作分布在不同的服务器上
  • 分布式事务需要保证不同服务器上的小操作要不全部失败,要不全部成功。本质上是要保证数据库数据一致性
  1. 为什么需要分布式事务
  • 当数据库单表一年产生数据超过1000W时就需要考虑分表,将一个数据库分成多个数据库。保证这些数据库的数据一致性就需要分布式事务
  • 应用业务服务化(SOA),将之前单机应用变成由服务组成的微服务系统。分离出入订单系统用户中心库存中心订单中心等。每个微服务都有自己的数据库。例如提交订单的操作,需要订单和库存同时操作,所以需要分布式事务保证下单成功库存减少,下单失败库存不变。

事务的特性ACID

  1. 原子性A
    整个事务中所有操作要不同时完成,要不全部失败
  2. 一致性C
    事务的执行必须保证系统的一致性,只要事务成功了,事务的操作就一定要体现在数据库中
  3. 隔离性I
    事务之间不会相互影响,一个事物的中间状态不会被其它事务感知
  4. 持久性D
    事务完成了之后事务对数据的操作就会完全保存在数据库中,停电宕机也会保存

基于XA协议的两阶提交

  1. 名词解释
  • XA是一个分布式事务协议,主要分为两个部分事务管理器本地资源管理器
  • 本地资源管理器通常由数据库实现,负责本地资源的提交和回滚
  • 事务管理器作为全局调度者,负责调度本地资源
  1. 步骤
  • 第一阶段:事务管理器通知各个本地资源管理器预备操作,本地资源管理器回复就绪(锁住资源)
  • 第二阶段:事务管理器通知本地资源管理器提交,本地资源管理器回复成功
  1. 优缺点
  • 优点是协议简单,大部分商业数据库(OracleDB2等)都实现了XA协议,使用分布式的成本较低。
  • 缺点是性能差,在mysql中支持不理想(没有prepare阶段日志,准备切换回导致主库和备库数据不一致),很多nosql也没有XA的支持。这些缺点也导致其应用狭隘

基于消息事务实现的最终一致性事务

  1. 解释
  • 消息事务就是基于消息中间件的两阶段提交,是对消息中间件的特殊利用。此方法实现的事务是最终一致性,并不能保证实时一致。
  1. 步骤
  • 系统A发送预备消息给中间件
  • 中间件保存预备消息并返回
  • 系统A执行本地事务
  • 执行结束后发送给消息中间件
  • 消息中间件保存信息并发送给其它系统
  • 其它系统执行本地事务并返回(如果失败中间件会重复发送)
  • mq将最终执行结果返回系统A
  1. 优缺点
  2. 性能好
  3. 缺点是如果其它系统一直不成功,会破坏一致性。

TCC编程模式

  • 根据业务决定具体代码实现
  • 一种编程框架,吧业务逻辑分为Try``Confirm``Cancel三个操作
  • 例如下单,Try就是去库存,Confirm是更新订单,Cancel是失败恢复库存
  • 本质是人为进行两段式提交