优秀开源项目

优秀开源项目

运维

Yearning SQL 审核平台

基于Inception的企业级web端 SQL审核平台。

官网

git
项目地址:
https://gitee.com/mirrors/Yearning

Spug 自动运维

Spug是面向中小型企业设计的轻量级无Agent的自动化运维平台,整合了主机管理、主机批量执行、主机在线终端、应用发布部署、在线任务计划、配置中心、监控、报警等一系列功能。

git
项目地址:
https://github.com/openspug/spug

演示地址 https://demo.spug.dev/home

特性

  • 批量执行: 主机命令在线批量执行
  • 在线终端: 主机支持浏览器在线终端登录
  • 文件管理: 主机文件在线上传下载
  • 任务计划: 灵活的在线任务计划
  • 发布部署: 支持自定义发布部署流程
  • 配置中心: 支持KV、文本、json等格式的配置
  • 监控中心: 支持站点、端口、进程、自定义等监控
  • 报警中心: 支持短信、邮件、钉钉、微信等报警方式
  • 优雅美观: 基于 Ant Design 的UI界面
  • 开源免费: 前后端代码完全开源
  • J-IM

J-IM 是用JAVA语言,基于t-io开发的轻量、高性能、单机支持几十万至百万在线用户IM,主要目标降低即时通讯门槛,快速打造低成本接入在线IM系统,通过极简洁的消息格式就可以实现多端不同协议间的消息发送如内置(Http、Websocket、Tcp自定义IM协议)等,并提供通过http协议的api接口进行消息发送无需关心接收端属于什么协议,一个消息格式搞定一切!

t-io https://www.tiocloud.com/tio.pdf

git

项目地址:
https://gitee.com/LuckyFishSpace/j-im

Skywalking 分布式链路追踪

Skywalking 分布式链路追踪

skywalking下载

在官网下载最新版skywalking:
下载地址

http://skywalking.apache.org/downloads/

解压后启动

启动skywalking后台程序和ui
在此之前可以修改ui等启动的端口。

例如ui的端口,在webapp目录下–>的webapp.yml文件,这里修改的是808端口,默认是8080

启动skywalking的方法是:

a.在解压下的bin目录,windows双击startup.bat,linux运行startup.sh

b.启动后检查端口

探针的配置

使用探针
在要监控的项目中使用探针,在开发环境,结合idea使用时,配置我们项目的启动参数:

1
-javaagent:D:\cloud\apache-skywalking-apm-bin\agent\skywalking-agent.jar

-javaagent后面时skywalking探针的绝对地址,也就是之前下载的skywalking的压缩包解压出来后目录下的agent目录下的skywalking-agent.jar包。

IDEA中使用 VM options中加入相同配置.

1
2
3
4
5
6
7
8
9
10
-javaagent:D:/apache-skywalking-apm-bin/agent/skywalking-agent.jar
-Dskywalking.collector.backend_service=localhost:12800

agent(实际地址)
IP 记得改

eg:

-javaagent:E:/微服务/skywalking/apache-skywalking-apm-bin-es7/agent/skywalking-agent.jar
-Dskywalking.collector.backend_service=172.16.129.139:12800

配置优先级

skywalking agent配置覆盖
在正式环境使用探针时,每使用一次,都去配置一下agent的参数信息也是比较繁琐的,所以skywalking有配置覆盖的选项,即不同的形式的配置优先级是不一样的,具体如下:

JVM配置 > 系统环境变量 > agent.config

上面所用的方式是agent.config的方式,优先级是最低的,所以还可以覆盖他的配置:
jvm配置:

1
2
-javaagent:/path/to/skywalking-agent.jar=[option1]=[value1],[option2]=[value2]
-javaagent:skywalkingagent.jar=agent.service_name=test,collector.backend_service=xxx:11800

环境变量配置:

1
2
-Dskywalking.[option1]=[value1]
-Dskywalking.service_name=xxxx

这样在启动的时候,就不用每次去复制一份agent文件了,只需要在启动时加上参数即可

skywalking修改数据源

skywalking 版本 8.0, 对应使用mysql8.0,所以复制相应驱动至 skywalking\oap-libs

1
mysql-connector-java-8.0.21.jar

SpringCloud Config配置中心

SpringCloud Config配置中心

简介

在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,
它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是Config-Server,二是Config-Client。

Config Server从本地读取配置文件

创建一个spring-boot项目,取名为 config-server,依赖如下:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

注解 @EnableConfigServer 开启配置服务器

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableConfigServer //开启配置服务器
public class ConfigServerApplication {

public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}

}

需要在程序的配置文件application.properties文件配置以下。通过 spring.profile.active=native 来配置 ConfigServer 从本地读取配置,读取的路径为 classpath 下的 shared 目录。

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 8769
spring:
application:
name: config-server
profiles:
active: native
cloud:
config:
server:
native:
search-locations: classpath:/shared

在 resources 目录下新建 shared 文件夹,在 shared 文件夹下新建 config-client-dev.yml 文件。

1
2
3
4
server:
port: 8762

foo: foo version 1

启动 config-server 工程!

构建Config-Client

创建一个spring-boot项目,取名为 config-client,依赖如下:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

在 resources 目录下新建 bootstrap.yml 文件,因为 bootstrap 相对于 application 具有优先的执行顺序。
变量{spring.application.name}和{spring.profiles.active},两者以“-”相连,构成了向 Config Server 读取的配置文件名。

1
2
3
4
5
6
7
8
9
spring:
application:
name: config-client
cloud:
config:
uri: http://localhost:8769
fail-fast: true #读取没有成功,执行快速失败
profiles:
active: dev

编写一个接口,测试读取配置文件的 foo 变量,并通过 API 接口返回.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@SpringBootApplication
@RestController
public class ConfigClientApplication {

public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}

@Value("${foo}")
String foo;

@RequestMapping(value = "/foo")
public String hi(){
return foo;
}
}

启动 config-client 工程,访问 http://localhost:8762/foo,显示

foo version 1

可见 config-client 成功向 config-server 工程读取了配置文件中 foo 变量的值。

Config Server从远程Git仓库读取配置文件

修改 config-server 的配置文件 application.yml,代码如下.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 8769
spring:
application:
name: config-server
cloud:
config:
label: master
server:
git:
uri: https://github.com/forezp/SpringcloudConfig
search-paths: respo
username: miles02@163.com
password:

如果Git仓库为公开仓库,可以不填写用户名和密码,如果是私有仓库需要填写,本例子是公开仓库,放心使用。

配置 解释
spring.cloud.config.server.git.uri 配置git仓库地址
spring.cloud.config.server.git.searchPaths 配置仓库路径
spring.cloud.config.label 配置仓库的分支
spring.cloud.config.server.git.username 访问git仓库的用户名
spring.cloud.config.server.git.password 访问git仓库的用户密码

远程仓库 https://github.com/xxxx/SpringcloudConfig/ 中有个文件config-client-dev.properties文件中有一个属性:

foo = foo version 2

但是没有规定 server.port 属性,所以会以默认 的 8080 启动,启动程序:访问http://localhost:8080/foo

foo version 2

可见,config-server 从远程 Git 仓库读取了配置文件,config-client 从config-server 读取了配置文件.

构建高可用的 Config Server

将配置中心 config-server 做成一个微服务,并且将其集群化,从而达到高可用。

config-client 在工程启动类上加上注解 @EnableEurekaClient,开启 EurekaClient的功能。

在配置文件 application.yml 加入相关配置,向 service-id 为 config-server 的配置服务读取配置文件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring:
application:
name: config-client
cloud:
config:
fail-fast: true
discovery:
enabled: true
service-id: config-server
profiles:
active: dev
server:
port: 8762
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/

启动 config-server、config-client 工程,访问 http://localhost:8762/foo,浏览器显:

foo version 2

只需要启动多个 config-server 实例即可搭建高可用的 config-server。

索引数据结构

https://www.cs.usfa.edu

索引数据结构
1.二叉树
2.红黑树
3.Hash表
4.B-Tree
5.B+Tree 非叶子节点不存储data,只存储索引,可以放更多的索引。顺序访问指针,提高区间访问性能。

MySQL索引底层支持两种:Hash和B+Tree

MyISAM
索引文件和数据文件是分离的,非聚集索引。
frm 表结构
MYD 行数据
MYI 索引文件

InnoDB索引实现(聚集索引)
1)表数据文件本身是按B+tree组织的一个索引结构文件
2)聚集索引-叶子结点包含了完整的数据记录
3)InnoDB表必须有主键,并且推荐使用整形的自增主键
(UUID vs 整型, 整型所需索引空间小,消耗的磁盘空间小,可以存放更多的索引,同时数据比较时更快)
4)非主键索引结构叶子结点存储的是主键值 (一致性和节省存储空间)

大数据

数据中台和数据仓库、数据平台的关键区别?

概括地说,三者的关键区别有以下几方面:

数据中台是企业级的逻辑概念,体现企业 D2V(Data to Value)的能力,为业务提供服务的主要方式是数据 API;

数据仓库是一个相对具体的功能概念,是存储和管理一个或多个主题数据的集合,为业务提供服务的方式主要是分析报表;

数据平台是在大数据基础上出现的融合了结构化和非结构化数据的数据基础平台,为业务提供服务的方式主要是直接提供数据集;

数据中台距离业务更近,为业务提供速度更快的服务;

数据仓库是为了支持管理决策分析,而数据中台则是将数据服务化之后提供给业务系统,不仅限于分析型场景,也适用于交易型场景;

数据中台可以建立在数据仓库和数据平台之上,是加速企业从数据到业务价值的过程的中间层。

数据仓库具有历史性,其中存储的数据大多是结构化数据,这些数据并非企业全量数据,而是根据需求针对性抽取的,

因此数据仓库对于业务的价值是各种各样的报表,但这些报表又无法实时产生。数据仓库报表虽然能够提供部分业务价值,但不能直接影响业务。

数据平台的出现是为了解决数据仓库不能处理非结构化数据和报表开发周期长的问题,所以先撇开业务需求、把企业所有的数据都抽取出来放到一起,成为一个大的数据集,其中有结构化数据、非结构化数据等。

当业务方有需求的时候,再把他们需要的若干个小数据集单独提取出来,以数据集的形式提供给数据应用。

而数据中台是在数据仓库和数据平台的基础上,将数据生产为为一个个数据 API 服务,以更高效的方式提供给业务。

编码规范

编码规范

C# 编码风格指南

一旦进入代码开发阶段,你必须安排好代码审查计划以确保每个人都遵守同样的规则。建议按以下3种方式进行代码审查:

  1. 互相审查 - 由其他团队成员进行代码审查以确保遵守了代码规范且达到要求。这种方式可以配合单元测试。项目中每个代码文件必须要通过这个程序。

  2. 架构审查 - 架构人员需对项目核心模块进行审查以确保符合设计,没有出现大的甚至有可能影响整个项目运转的纰漏。

  3. 团队审查 - 每周随机选择一个或多个文件进行一次团队审查。审查会议开始前30分钟,将文件打印并分发到每个成员手里,会议开始后用投影仪将文件内容展示出来。代码的每一块都要进行审查,让所有成员提出改进建议。(别忘了要感谢提供素材的开发人员,并确保他不会觉得受到了“群嘲”!)

代码文件组织

C# 源代码文件

每个源代码文件应该只包含一个类定义,也即类定义只出现在它自己的文件中。源代码文件名需与类声明里的类名保持一致。
譬如一个名为User的类的源文件名应该是User.cs。

版权声明

1
2
3
4
5
6
7
8
9
10
11
//-------------------------------------------------------------------------------
// Copyright (c) Dick. All Right Reserved.
// This source is subject to the Microsoft Permissive License.
// Please see the License.txt file for more information.
// All other rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//-------------------------------------------------------------------------------

目录设计

每层命名空间都要建立目录(以MyProject.UI.Admin为例,请使用MyProject/UI/Admin这样的目录结构,而不是只建立一层名为MyProject.UI.Admin的目录)。

代码排列

C#源代码文件整体顺序应为:

1
2
3
Using 声明
Namespace声明
Class 和 interface 声明

C#类内部分的顺序应为:

1
2
3
4
5
成员变量
属性
构造函数
方法
`

以上每个部分都需要放到 #region里。

命名空间和USING声明

  1. Using 和命名空间声明都要左边界齐平。
  2. 命名空间的每个组件名首字母要大写
  3. 如果组件名是个缩写,只让第一个字母大写,如 System.Data.Sql。
  4. 如果这个缩写只有2个字母,那可以2个字母都大写,如 System.IO。

注意: 移除不需要的或重复的命名空间,使用短的命名空间来代替。譬如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Preferred
using System.Data.SqlClient;
public void ValidationCall(){

SqlConnection conn = new SqlConnection(connstr);

}

// Avoid
public void ValidationCall(){

System.Data.SqlClient.SqlConnection conn = new SqlConnection(connstr);

}

创建和修订记录

文件创建和修订记录需要按以下格式填写到using代码段后面,namespace之前。

1
2
3
4
5
/*
* 作者: 创建日期:
* 修订者: 修订日期: 修订内容:
*
*/

XML 文档

Visual Studio提供了一种文档类型,在开发环境中可以用来检测并导出到结构化的XML中,以用来创建与源代码分离的代码级别的文档。
XML 文档用于描述类、方法和属性。它应当在所有可用的情况下尽可能使用。

缩进

空行

适当的空行能增强代码可读性。它们能帮助区分逻辑不相干的代码片段。
双行空行要放在:
源代码中逻辑不相关的代码片段之间
不同的类和接口定义(如果有时候不得不放在同个文件,尽量避免这种情况)
单行空行要放在:

  • 方法与方法之间
  • 属性与属性之间
  • 方法体内的局部变量和它的第一次使用之间
  • 方法体内不同逻辑片段之间以增强可读性

换行

如果一个表达式在一行内放不下,则根据以下常用原则来进行换行:

  1. 操作符号之后换行
  2. 逗号之后换行
  3. 择较高等的换行,其次才是较低等的换行 (譬如优先对括号外的部分换行)
    换行后需要缩进
    1
    2
    3
    4
    5
    6
    7
    // 调用方法
    SomeMethod1(expression1, expression2, expression3, expression4, expression5);
    // 声明方法
    void SomeMethod1(long Expression1, long Expression2, long Expression3, long Expression4, long Expression5)
    {
    //...
    }

注意: 换行后第二行的缩进与第一个参数齐平
以下是一个数学表达式换行的例子。第一种换行方式是较好的,因为是在括号以外的地方进行的换行,也即是在较高等进行的分行。

1
2
3
4
5
// Preferred
var1 = var2 * (var3 + var4 – var5) +
var4 * var6;
// Avoid
var1 = var2 * (var3 + var4 - var5) + var4 * var6;

对if表达式的分行需要使用缩进:

1
2
3
4
if ((condition1 && condition2) || (condition3 && condition4) && condition5 || !condition6)
{
...
}

三元表达式请用这两种格式:

1
2
3
4
Alpha = aLongBooleanExpression ? beta : gamma;
Alpha = aLongBooleanExpression ?
beta :
gamma;

代码间距

空格间距

在代码中间的逗号或分号后应该有单独的一个空格,此外一个关键字与后跟的括号之间也要有个空格,譬如:

1
2
3
4
5
6
7
8
9
10
// Correct
TestMethod(a, b, c);
while (condition)
{
//...
}

// Avoid
TestMethod(a, b, c);
TestMethod(a, b, c);

注意: 在方法名和它的前括号之间不需要有空格。这样便于区分是关键字还是方法名。
操作符两边要加上空格(++或逻辑非这样的一元运算符除外),譬如:

1
2
3
4
5
6
7
a = b; // Avoid a=b;

// Avoid for(int i=0; i<10; ++i) or for(int i=0;i<10;++i)
for (int i = 0; i < 10; ++i)
{
...
}

TAB间距

该使用几个空格作为代码缩进距离从来没有达成一致过。有人喜欢2个空格,有人喜欢4个而有人喜欢8个甚至更多。所以最好使用Tab缩进。它的好处有:
可以自定义缩进距离
它只需一个字符,所以只要按一次键而不是2,4,8…

如果你要增加缩进(或减少)一片代码,可以选中它们然后按Tab来增加或者Shift+Tab来减少缩进。几乎所有文本编辑器都支持这个操作。
在这里Tab指的就是标准的缩进字符。
注意: 不要使用空格来缩进代码-用Tab!在VS中把Tab配置成4个空格的距离。

一般注释

通用注释前缀

TODO : 表示以后别忘了在这里还需要进一步处理
BUG: [bugid]:表示这里有个已知bug,解释一下bug情况,如果可以则给出bug id。
KLUDGE: 表示这里的代码有点糟糕,解释一下有时间的话将如何改进。
TRICKY: 表示以下代码比较奇技淫巧,如果没有仔细思考请不要改动。
WARNING: 表示当心某事
COMPILER: 表示临时注释掉某些影响编译通过的代码。这些问题最终会被解决的。
ATTRIBUTE: value:嵌在注释中的属性的一般形式。你可以自定义属性,它们最终会被VS提取。

单行注释

1
2
//Console.WriteLine("哈哈");
//Console.ReadKey();

多行注释

1
2
3
4
/*
* 注释内容
* 注释内容
*/

文档注释

1
2
3
4
/// <summary>
/// 文档注释
/// 文档注释
/// </summary>

变量声明及命名规范

变量:

Camel:变量名首单词的首字母小写,其余每个单词首字母单词大写,多用于给变量或字段或方法参数命名。  

1
2
3
var str = "123";
var highSchoolStudent = "123";
int num = 5;

常量:

1
public static const string BaseIpAddress = "";

方法名:

Pascal:每个单词的首字母都要大些其余小写,多用于类或方法。尽量用一般名词或动名词。

1
2
3
4
public string GetHighSchoolStudent()
{

}

Java 并发进阶常见面试题总结

Java 并发进阶常见面试题总结

synchronized 关键字

synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。
(后半句) 任意时刻只有一个线程在访问

在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的。如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的 synchronized 效率低的原因。庆幸的是在 Java 6 之后 Java 官方对从JVM层面对synchronized较大优化,所以现在的 synchronized 锁效率也优化得很不错了。JDK1.6对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。

项目中怎么使用 synchronized 关键字

synchronized关键字最主要的三种使用方式:

修饰实例方法:
作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁

修饰静态方法: 也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态资源,不管new了多少个对象,只有一份)。所以如果一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。

修饰代码块: 指定加锁对象,对给定对象加锁,进入同步代码块前要获得给定对象的锁。

比如以下的:双重校验锁实现单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Singleton {

private volatile static Singleton uniqueInstance;

private Singleton() {
}

public static Singleton getUniqueInstance() {
//先判断对象是否已经实例过,没有实例化过才进入加锁代码
if (uniqueInstance == null) {
//类对象加锁
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}


另外,需要注意 uniqueInstance 采用 volatile 关键字修饰也是很有必要。

uniqueInstance 采用 volatile 关键字修饰也是很有必要的, uniqueInstance = new Singleton(); 这段代码其实是分为三步执行:

  • 为 uniqueInstance 分配内存空间
  • 初始化 uniqueInstance
  • 将 uniqueInstance 指向分配的内存地址

但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出先问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。

使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。

字符型常量和字符串常量的区别

字符型常量和字符串常量的区别

形式上: 字符常量是单引号引起的一个字符; 字符串常量是双引号引起的若干个字符

含义上: 字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)

占内存大小: 字符常量只占2个字节; 字符串常量占若干个字节(至少一个字符结束标志) (注意: char在Java中占两个字节)

数据类型 大小 包装器类型
boolean - Boolean
char 16bits Character
byte 8bits Byte
short 16bits Short
int 32bits Integer
long 64bits Long
float 32bits Float
double 64bits Double

Java 并发基础常见面试题总结

进程和线程

何为进程?

进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。

在 Java 中,当我们启动 main 函数时其实就是启动了一个 JVM 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程。

何为线程?

与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

一个 Java 程序的运行是 main 线程和多个其他线程同时运行。

一个进程中可以有多个线程,多个线程共享进程的堆和方法区,但是每个线程有自己的程序计数器、虚拟机栈 和 本地方法栈。

进程间彼此相对独立,线程则不一定,因为同一进程中的线程极有可能会相互影响。

为什么程序计数器、虚拟机栈和本地方法栈是线程私有的呢?为什么堆和方法区是线程共享的呢?

程序计数器为什么是私有的?

程序计数器主要有下面两个作用:

1.字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。

2.在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。

需要注意的是,如果执行的是 native 方法,那么程序计数器记录的是 undefined 地址,只有执行的是 Java 代码时程序计数器记录的才是下一条指令的地址。

所以,程序计数器私有主要是为了线程切换后能恢复到正确的执行位置。

虚拟机栈和本地方法栈为什么是私有的?

虚拟机栈: 每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。

本地方法栈: 和虚拟机栈所发挥的作用非常相似,区别是: 虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。

所以,为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地方法栈是线程私有的。

一句话简单了解堆和方法区

堆和方法区是所有线程共享的资源,其中堆是进程中最大的一块内存,主要用于存放新创建的对象
(所有对象都在这里分配内存),方法区主要用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

说说并发与并行的区别?

并发: 同一时间段,多个任务都在执行 (单位时间内不一定同时执行);
并行: 单位时间内,多个任务同时执行。

为什么要使用多线程呢?

先从总体上来说:

计算机底层角度: 线程可以比作是轻量级的进程,是程序执行的最小单位,线程间的切换和调度的成本远远小于进程。另外,多核 CPU 时代意味着多个线程可以同时运行,这减少了线程上下文切换的开销。+

当代互联网发展趋势角度: 现在的系统动不动就要求百万级甚至千万级的并发量,而多线程并发编程正是开发高并发系统的基础,利用好多线程机制可以大大提高系统整体的并发能力以及性能。

再深入到计算机底层来探讨:

单核时代: 在单核时代多线程主要是为了提高 CPU 和 IO 设备的综合利用率。举个例子:当只有一个线程的时候会导致 CPU 计算时,IO 设备空闲;进行 IO 操作时,CPU 空闲。我们可以简单地说这两者的利用率目前都是 50%左右。但是当有两个线程的时候就不一样了,当一个线程执行 CPU 计算时,另外一个线程可以进行 IO 操作,这样两个的利用率就可以在理想情况下达到 100%了。

多核时代: 多核时代多线程主要是为了提高 CPU 利用率。举个例子:假如我们要计算一个复杂的任务,我们只用一个线程的话,CPU 只会一个 CPU 核心被利用到,而创建多个线程就可以让多个 CPU 核心被利用到,这样就提高了 CPU 的利用率。

多线程带来的问题

并发编程的目的就是为了能提高程序的执行效率提高程序运行速度,但是并发编程并不总是能提高程序运行速度的,而且并发编程可能会遇到很多问题,比如:内存泄漏、上下文切换、死锁还有受限于硬件和软件的资源闲置问题。

线程的生命周期和状态?

线程创建之后它将处于 NEW(新建) 状态,调用 start() 方法后开始运行,线程这时候处于 READY(可运行) 状态。可运行状态的线程获得了 CPU 时间片(timeslice)后就处于 RUNNING(运行) 状态。

当线程执行 wait()方法之后,线程进入WAITING(等待)状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而TIME_WAITING(超时等待)状态相当于在等待状态的基础上增加了超时限制,比如通过 sleep(long millis)方法或 wait(long millis)方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。线程在执行 Runnable 的run()方法之后将会进入到 TERMINATED(终止) 状态。

上下文切换

多线程编程中一般线程的个数都大于 CPU 核心的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换。(时间片策略)

概括来说就是:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换会这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。

上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间。所以,上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。

Linux 相比与其他操作系统(包括其他类 Unix 系统)有很多的优点,其中有一项就是,其上下文切换和模式切换的时间消耗非常少。

Greenplum 从入门到放弃 四

Greenplum 从入门到放弃(四)

PostgreSQL与Greenplum的关系

PostgreSQL

PostgreSQL是一种非常先进的对象–关系型数据库管理系统(ORDBMS),是目前功能最强大,特性最丰富和技术最先进的自由软件数据库系统之一,其某些特性甚至连商业数据库都不具备。

PostgreSQL的特点可以说是数不胜数,称其为最先进的开
源软件数据库当之无愧,支持绝大部分的主流数据库特性,主
要体现在如下几方面:

  1. 函数/存储过程

PostgreSQL对非常丰富的过程类语言提供支持,可以编写自定义函数/存储过程

  • 内置的plpgsql,一种类似Oracle的PLsql的语言
  • 支持的脚本语言有:PL/Lua、PL/LOLCODE、PL/Perl、PL/HP、PL/Python、PL/Ruby、PL/sh、PL/Tcl和PL/Scheme。、
  • 编译语言有C、C++和JAVA。
  • ·统计语言PL/R
  1. 索引

PostgreSQL支持用户定义的索引访问方法,并且内置了Btree、哈希和GiST索引。PostgreSQL中的索引有下面几个特点:

  • 可以从后向前扫描
  • 可以创建表达式索引
  • 部分索引
  1. 触发器

触发器是由SQL查询的动作触发的事件。比如,一个INSERT查询可能激活一个检查输入值是否有效的触发器。大多数触发器都只对INSERT或者UPDATE查询有效。PostgreSQL完全支持触发器,可以附着在表上,但是不能在视图上。不过视图可以有规则。多个触发器是按照字母顺序触发的。我们还可以用其他过程语言书写触发器函数,不仅仅PL/PgSQL。

  1. 并发管理(MVCC)

PostgreSQL的并发管理使用的是一种叫做“MVCC”(多版本并发机制)的机制,这种机制实际上就是现在在众多所谓的编程语言中极其火爆的“Lock Free”,其本质是通过类似科幻世界的时空穿梭的原理,给予每个用户一个自己的“时空”
,然后通过原子的“时空”控制来控制时间基线,并以此控制并发更改的可见区域,从而实现近乎无锁的并发,而同时还能在很大程度上保证数据库的ACID特性。

  1. 规则(RULE)

规则允许我们对由一个查询生成的查询树进行改写。

  1. 数据类型

PostgreSQL支持非常广泛的数据类型,包括:

  • 任意精度的数值类型;
  • 无限长度的文本类型;
  • 几何原语;
  • IPv4和IPv6类型;
  • CIDR块和MAC地址;
  • 数组。

用户还可以创建自己的类型,并且可以利用GiST框架把这些类型做成完全可索引的,比如来自PostGIS的地理信息系统(GIS)的数据类型。

  1. 用户定义对象

因为PostgreSQL使用一种基于系统表的可扩展的结构设计,所以PostgreSQL内部的几乎所有对象都可以由用户定义,这些对象包括:

  • 索引;
  • 操作符(内部操作符可以被覆盖);
  • 聚集函数;
  • 域;
  • 类型转换;
  • 编码转换。
  1. 继承

PostgreSQL的表是可以相互继承的。一个表可以有父表,
父表的结构变化会导致子表的结构变化,而对子表的插入和数
据更新等也会反映到父表中。

  1. 其他特性与扩展
  • 二进制和文本大对象存储;
  • 在线备份;
  • TOAST(The Oversized-Attribute Storage Technique)用于透明地在独立的地方保存大的数据库属性,当数据超过一定大小的时候,会自动进行压缩以节省空间;
  • 正则表达式。

此外PostgreSQL还有大量的附加模块和扩展版本,比如,多种不同的主从/主主复制方案:

  • Slony-I;
  • pgcluster;
  • Mammoth replicator;
  • Bucardo。