Mysql的数据类型

Mysql中定义的数据类型对数据库的优化是非常重要的。

mysql支持多种类型,大致可以分为三类:数值,日期/时间和字符串(字符)类型

1.数值类型

mysql支持所有的sql数值数据类型。这些数据包括严格数值数据类型(INTEGER,SMALLINT,DECIMAL,NUMERIC),以及近似数据类型(FLOAT,REAL和DOUBLE)

img

2.日期/时间类型

表示时间值得日期和时间类型为DATETIME,DATE,TIMESTAMP,TIME和YEAR。

每一个时间类型有一个有效值范值。

3.字符类型

CHAR—-定长字符串

VARCHAR—-变长字符串

BINARY–

注意:

1.Mysql一个汉字占多少长度和编码有关:

UTF-8:一个汉字=3个字节

GBK: 一个汉字=2个字节

2.varchar(n)表示n个字符,无论汉字和英文,mysql都能存n个字符,仅是实际字节长度有所区别。

3.mysql检查长度,length()函数来实现。

总结:

1、整型

MySQL数据类型 含义(有符号)
tinyint(m) 1个字节 范围(-128~127)
smallint(m) 2个字节 范围(-32768~32767)
mediumint(m) 3个字节 范围(-8388608~8388607)
int(m) 4个字节 范围(-2147483648~2147483647)
bigint(m) 8个字节 范围(+-9.22*10的18次方)

取值范围如果加了 unsigned,则最大值翻倍,如 tinyint unsigned 的取值范围为(0~255)。

2、浮点型(float 和 double)

MySQL数据类型 含义
float(m,d) 单精度浮点型 8位精度(4字节) m总个数,d小数位
double(m,d) 双精度浮点型 16位精度(8字节) m总个数,d小数位

设一个字段定义为 float(5,3),如果插入一个数 123.45678,实际数据库里存的是 123.457,但总个数还以实际为准,即 6 位。

3、定点数

浮点型在数据库中存放的是近似值,而定点类型在数据库中存放的是精确值。

decimal(m,d) 参数 m<65 是总个数,d<30 且 d<m 是小数位。

4、字符串(char,varchar,_text)

MySQL数据类型 含义
char(n) 固定长度,最多255个字符
varchar(n) 可变长度,最多65535个字符
tinytext 可变长度,最多255个字符
text 可变长度,最多65535个字符
mediumtext 可变长度,最多2的24次方-1个字符
longtext 可变长度,最多2的32次方-1个字符

char 和 varchar:

  • 1.char(n) 若存入字符数小于n,则以空格补于其后,查询之时再将空格去掉。所以 char 类型存储的字符串末尾不能有空格,varchar 不限于此。
  • 2.char(n) 固定长度,char(4) 不管是存入几个字符,都将占用 4 个字节,varchar 是存入的实际字符数 +1 个字节(n<=255)或2个字节(n>255),所以 varchar(4),存入 3 个字符将占用 4 个字节。
  • 3.char 类型的字符串检索速度要比 varchar 类型的快。

varchar 和 text:

  • 1.varchar 可指定 n,text 不能指定,内部存储 varchar 是存入的实际字符数 +1 个字节(n<=255)或 2 个字节(n>255),text 是实际字符数 +2 个字节。
  • 2.text 类型不能有默认值。
  • 3.varchar 可直接创建索引,text 创建索引要指定前多少个字符。varchar 查询速度快于 text, 在都创建索引的情况下,text 的索引似乎不起作用。

mysql的存储过程

简介:
什么是存储过程:一组sql语句集,功能强大,可以实现一些比较复杂的逻辑功能,类似java程序中的方法。

mysql 5.0以前并不支持存储过程,在5.0以后支持存储过程,可以大大提高数据库的处理过程,同时可以提高

是主动调用的,功能比触发器更加强大。

存储过程的优点:

1.增强SQL语言的功能和灵活性。

2.标准组件式编程。

3.较快的执行速度。

存储过程的创建:
存储过程的三个参数:IN OUT INOUT

存储过程根据需要会有输入,输出,输入输出参数,如果有多个参数用, 分割开。存储过程的参数用在存储过程的定义。

IN参数的值必须在调用存储过程时指定,在存储过程中修改该参数的值不能被返回,为默认值

OUT**:**该值可在存储过程内部被改变,并可返回

INOUT**:**调用时指定,并且可被改变和返回

存储过程使用的案例:(只保留近三天的数据)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
BEGIN
#Routine body goes here...
DECLARE s int DEFAULT 0;
DECLARE tableName VARCHAR(32);
DECLARE c_tableNames CURSOR FOR select table_name from information_schema.tables where table_schema='ikms_exec' and table_name NOT LIKE 'base%' AND table_name LIKE '%_his';
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET s=1;
set @timenow=DATE_ADD(NOW(),INTERVAL -3 DAY);
OPEN c_tableNames;
while s <> 1 DO
FETCH c_tableNames into tableName;
SET @v_sql=CONCAT("DELETE FROM ",tableName," WHERE create_time<'",@timenow,"'");
prepare stmt from @v_sql;
EXECUTE stmt;
deallocate prepare stmt;
end while;
CLOSE c_tableNames;
END

如何执行存储过程:CALL procedure_name()

mysql存储过程的查询:

select name from mysql.proc where db = “数据库名”

修改 Alert

删除 DROP

MYSQL 创建视图

什么是视图:

select 语句返回的结果。

视图的特性:

视图是若张基本表的引用,一张虚拟表;查询语句执行的结果,不存储具体的数据,只能做查询,基本的表数据发生了变化,视图就跟着发生变化。

视图的作用:

方便操作,特别是查询操作,减少负责的SQL语句,增强可读性;

视图的优点:

1.使用视图可以执行用户数据,聚集特定数据。

2.使用视图,可以简化数据操作。

3.使用视图,基表的数据就有一定的安全性,因为视图是虚拟的,物理上是不存在的,只是存储

4.可以合并分离的数据,创建分区视图。

视图的缺点:

1性能差:需要将对视图查询转化成对基表的查询,需要花费一点的时间。

2.修改权限:如果需要修改视图的某些信息后,数据库必须把它转化成对基本表的某些信息的修改。

使用场合

多表复杂查询的时候,返回用户特性信息时。

MYSQL索引

mysql目前支持的索引有:普通索引,唯一索引,主键索引,组合索引,全文唯一

普通索引

最基本的索引,没有任何限制 INDEX

唯一索引

唯一索引的值必须是唯一的,但是允许有空值。如果是组合索引的话,则列值的组合必须是唯一,它有以下几种创建方式。 UNION INDEX

主键索引

主键索引是特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般在创建表的时候创建主键索引。

组合索引

指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。遵循最左原则。

全文索引

主要用来查找文本的关键字,而不是直接与索引中的值相比较。 FULLTEXT

FULLTEXT索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。FULLTEXT索引配合match against操作使用,而不是一般的where语句加like.目前只支持 char,varchar,text列上可以创建全文索引

索引的优缺点

1.虽然索引提高了查询速度,同时会降低更新表的速度。 因为更新表时,不仅要保存数据,还要保存一下索引文件。

2.建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会增长很快。

MYSQL优化

1.sql语句优化:

(1)使用limit对查询结果的记录进行限定
(2)避免select *,将需要查找的字段列出来
(3)使用连接(join)来代替子查询
(4)拆分大的delete或insert语句

2.选择合适的数据类型

1)使用可存下数据的最小的数据类型,整型 < date,time < char,varchar < blob
(2)使用简单的数据类型,整型比字符处理开销更小,因为字符串的比较更复杂。如,int类型存储时间类型,bigint类型转ip函数
(3)使用合理的字段属性长度,固定长度的表会更快。使用enum、char而不是varchar
(4)尽可能使用not null定义字段
(5)尽量少用text,非用不可最好分表

3.选择合适的索引列

(1)查询频繁的列,在where,group by,order by,on从句中出现的列
(2)where条件中<,<=,=,>,>=,between,in,以及like 字符串+通配符(%)出现的列
(3)长度小的列,索引字段越小越好,因为数据库的存储单位是页,一页中能存下的数据越多越好
(4)离散度大(不同的值多)的列,放在联合索引前面。查看离散度,通过统计不同的列值来实现,count越大,离散程度越高:

1
mysql> SELECT COUNT(DISTINCT column_name) FROM table_name;
4.用命令分析

(1)SHOW查看状态
1.显示状态信息

1
mysql> SHOW [SESSION|GLOBAL] STATUS LIKE '%Status_name%';

session(默认):取出当前窗口的执行
global:从mysql启动到现在
(a)查看查询次数(插入次数com_insert、修改次数com_insert、删除次数com_delete)

1
mysql> SHOW STATUS LIKE 'com_select';

(b)查看连接数(登录次数)

1
mysql> SHOW STATUS LIKE 'connections';

(c)数据库运行时间

1
mysql> SHOW STATUS LIKE 'uptime';

(d)查看慢查询次数

1
mysql> SHOW STATUS LIKE 'slow_queries';

(e)查看索引使用的情况:

1
mysql> SHOW STATUS LIKE 'handler_read%';

handler_read_key:这个值越高越好,越高表示使用索引查询到的次数。
handler_read_rnd_next:这个值越高,说明查询低效。

2.显示系统变量

1
mysql> SHOW VARIABLES LIKE '%Variables_name%';

3.显示InnoDB存储引擎的状态

1
mysql> SHOW ENGINE INNODB STATUS;

(2)EXPLAIN分析查询

1
mysql> EXPLAIN SELECT column_name FROM table_name;

explain查询sql执行计划,各列含义:
table:表名;
type:连接的类型
​ -const:主键、索引;
​ -eq_reg:主键、索引的范围查找;
​ -ref:连接的查找(join)
​ -range:索引的范围查找;
​ -index:索引的扫描;
​ -all:全表扫描;
possible_keys:可能用到的索引;
key:实际使用的索引;
key_len:索引的长度,越短越好;
ref:索引的哪一列被使用了,常数较好;
rows:mysql认为必须检查的用来返回请求数据的行数;
extra:using filesort、using temporary(常出现在使用order by时)时需要优化。
​ -Using filesort 额外排序。看到这个的时候,查询就需要优化了
​ -Using temporary 使用了临时表。看到这个的时候,也需要优化
(3)PROFILING分析SQL语句
1.开启profile。查看当前SQL执行时间

1
2
mysql> SET PROFILING=ON; 
mysql> SHOW profiles;

2.查看所有用户的当前连接。包括执行状态、是否锁表等

1
mysql> SHOW processlist;

(4)PROCEDURE ANALYSE()取得建议
通过分析select查询结果对现有的表的每一列给出优化的建议

1
mysql> SELECT column_name FROM table_name PROCEDURE ANALYSE();

(5)OPTIMIZE TABLE回收闲置的数据库空间

1
mysql> OPTIMIZE TABLE table_name;

对于MyISAM表,当表上的数据行被删除时,所占据的磁盘空间并没有立即被回收,使用命令后这些空间将被回收,并且对磁盘上的数据行进行重排(注意:是磁盘上,而非数据库)。
对于InnoDB表,OPTIMIZE TABLE被映射到ALTER TABLE上,这会重建表。重建操作能更新索引统计数据并释放成簇索引中的未使用的空间。
只需在批量删除数据行之后,或定期(每周一次或每月一次)进行一次数据表优化操作即可,只对那些特定的表运行。
(6)REPAIR TABLE修复被破坏的表

1
mysql> REPAIR TABLE table_name;

(7)CHECK TABLE检查表是否有错误

1
mysql> CHECK TABLE table_name;

1 对查询进行优化,要尽量避免全表扫描,应考虑在where及order by 涉及的列上建立索引。

2 应尽量避免在where子句中对字段进行null值判断,否则会导致引擎放弃使用索引而进行全表扫描。

最好不要给数据库留NULL,尽可能的使用NOT NULL填充数据库。

备注,评论,描述 之类的可以设置为NULL,其他的,最好不要使用NULL。

不要以为 NULL 不需要空间,比如:char(100) 型,在字段建立时,空间就固定了, 不管是否插入值(NULL也包含在内),都是占用 100个字符的空间的,如果是varchar这样的变长字段, null 不占用空间。

可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:

1
select id from t where num = 0

3 应尽量避免在where字句中使用!=或<>操作符,否则引擎放弃使用索引而进行全表扫描。

4 应尽量避免在where字句中使用or来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描。

1
select id from t where num=10 or Name = 'admin'

可以这样查询:

1
2
3
select id from t where num = 10
union all
select id from t where Name = 'admin'

5.in 和 not in 也要慎用,否则会导致全表扫描,如

1
select id from t where num in(1,2,3)

对于连续的数值,能用 between 就不要用 in 了:

1
select id from t where num between 1 and 3

很多时候用 exists 代替 in 是一个好的选择:

1
select num from a where num in(select num from b)

用下面的语句替换:

1
select num from a where exists(select 1 from b where num=a.num)

6.下面的查询也将导致全表扫描:

1
select id from t where name like ‘%abc%’

若要提高效率,可以考虑全文检索。

7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

1
select id from t where num = @num

可以改为强制查询使用索引:

1
select id from t with(index(索引名)) where num = @num

8 .应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

1
select id from t where num/2 = 100

应改为:

1
select id from t where num = 100*2

9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:

1
2
select id from t where substring(name,1,3) = ’abc’       -–name以abc开头的id
select id from t where datediff(day,createdate,’2005-11-30′) = 0 -–‘2005-11-30’ --生成的id

应改为:

1
2
select id from t where name like 'abc%'
select id from t where createdate >= '2005-11-30' and createdate < '2005-12-1'

10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。

12.不要写一些没有意义的查询,如需要生成一个空表结构:

1
select col1,col2 into #t from t where 1=0

这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:

1
create table #t(…)

13.Update 语句,如果只更改1、2个字段,不要Update全部字段,否则频繁调用会引起明显的性能消耗,同时带来大量日志。

14.对于多张大数据量(这里几百条就算大了)的表JOIN,要先分页再JOIN,否则逻辑读会很高,性能很差。

15.select count(*) from table;这样不带任何条件的count会引起全表扫描,并且没有任何业务意义,是一定要杜绝的。

16.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。

17.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。

18.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连 接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

19.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

20.任何地方都不要使用 select from t ,用具体的字段列表代替“”,不要返回用不到的任何字段。

21.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。

22.避免频繁创建和删除临时表,以减少系统表资源的消耗。临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件, 最好使用导出表。

23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时 间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。

28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

29.尽量避免大事务操作,提高系统并发能力。

30.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

实际案例分析:拆分大的 DELETE 或INSERT 语句,批量提交SQL语句

如果你需要在一个在线的网站上去执行一个大的 DELETE 或 INSERT 查询,你需要非常小心,要避免你的操作让你的整个网站停止相应。因为这两个操作是会锁表的,表一锁住了,别的操作都进不来了。

Apache 会有很多的子进程或线程。所以,其工作起来相当有效率,而我们的服务器也不希望有太多的子进程,线程和数据库链接,这是极大的占服务器资源的事情,尤其是内存。

如果你把你的表锁上一段时间,比如30秒钟,那么对于一个有很高访问量的站点来说,这30秒所积累的访问进程/线程,数据库链接,打开的文件数,可能不仅仅会让你的WEB服务崩溃,还可能会让你的整台服务器马上挂了。

所以,如果你有一个大的处理,你一定把其拆分,使用 LIMIT oracle(rownum),sqlserver(top)条件是一个好的方法。下面是一个mysql示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
while(1){

  //每次只做1000条

   mysql_query(“delete from logs where log_date <= ’2012-11-01’ limit 1000”);

  if(mysql_affected_rows() == 0){

     //删除完成,退出!
     break;
  }

//每次暂停一段时间,释放表让其他进程/线程访问。
usleep(50000)

}

安装好第一步一定是配置用户名和邮箱.

配置用户名邮箱

1
2
$ git config --global user.name "yuanjinxiugithub"
$ git config --global user.email 1871022901@qq.com

再次强调,如果使用了 –global 选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git 都会使用那些信息。 当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运行没有 –global 选项的命令来配置。

检查配置

如果想要检查你的配置,可以使用 git config –list 命令来列出所有 Git 当时能找到的配置。

$ git config –list
core.symlinks=false
core.autocrlf=true
core.fscache=true
color.diff=auto
color.status=auto
color.branch=auto
color.interactive=true
help.format=html
rebase.autosquash=true
http.sslcainfo=D:/Git/mingw64/ssl/certs/ca-bundle.crt
http.sslbackend=openssl
diff.astextplain.textconv=astextplain
filter.lfs.clean=git-lfs clean – %f
filter.lfs.smudge=git-lfs smudge – %f
filter.lfs.required=true
filter.lfs.process=git-lfs filter-process
credential.helper=manager
user.name=yuanjinxiu
user.email=1871022901@qq.com
credential.helper=store

Git帮助

1
2
3
若你使用 Git 时需要获取帮助,有三种方法可以找到 Git 命令的使用手册:
$ git help <verb>
$ git <verb> --help

例如,要想获得 config 命令的手册,执行

1
$ git help config

Git安装

Linux 安装Git:

1
2
3
4
5
6
7
8
检查是否安装Git
git --version

在 Ubuntu 这类 Debian 体系的系统上,可以用 apt-get 安装:
sudo apt-get install git

CentOs:
yum install -y git

window安装比较简单不多说。

Git帮助

1
2
3
若你使用 Git 时需要获取帮助,有三种方法可以找到 Git 命令的使用手册:
$ git help <verb>
$ git <verb> --help

例如,要想获得 config 命令的手册,执行

1
$ git help config

获取 Git 仓库

有两种取得 Git 项目仓库的方法。

  • 第一种是在现有项目或目录下导入所有文件到 Git 中
  • 第二种是从一个服务器克隆一个现有的 Git 仓库

在现有目录初始化

1
$ git init

该命令将创建一个名为 .git 的子目录,这个子目录含有你初始化的 Git 仓库中所有的必须文件,这些文件是 Git 仓库的骨干。 但是,在这个时候,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪。

克隆现有的仓库

如果你想获得一份已经存在了的Git仓库的拷贝,比如说,你想为某个开源项目贡献自己的一份力,这时就要用到 git clone 命令。

克隆仓库的命令格式是 git clone [url] 。

1
$ git clone git@172.16.5.77:shengwangzhong/hexo-blog.git

这会在当前目录下创建一个名为 “hexo-blog” 的目录,并在这个目录下初始化一个 .git 文件夹,从远程仓库拉取下所有数据放入.git文件夹,然后从中读取最新版本的文件的拷贝。 如果你进入到这个新建的hexo-blog文件夹,你会发现所有的项目文件已经在里面了,准备就绪等待后续的开发和使用。 如果你想在克隆远程仓库的时候,自定义本地仓库的名字,你可以使用如下命令:

1
$ git clone git@172.16.5.77:shengwangzhong/hexo-blog.git your-folder-name

这将执行与上一个命令相同的操作,不过在本地创建的仓库名字变为 your-folder-name。

Git 支持多种数据传输协议。 ssh\https\gi

记录每次更新到仓库

现在我们手上有了一个真实项目的 Git 仓库,并从这个仓库中取出了所有文件的工作拷贝。 接下来,对这些文件做些修改,在完成了一个阶段的目标之后,提交本次更新到仓库。

请记住,你工作目录下的每一个文件都不外乎这两种状态:已跟踪或未跟踪。

已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后,它们的状态可能处于未修改,已修改或已放入暂存区。 工作目录中除已跟踪文件以外的所有其它文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有放入暂存区。 初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态。

编辑过某些文件之后,由于自上次提交后你对它们做了修改,Git 将它们标记为已修改文件。 我们逐步将这些修改过的文件放入暂存区,然后提交所有暂存了的修改,如此反复。所以使用 Git 时文件的生命周期如下:

image

1
2
3
4
检查当前文件状态:
$ git status
On branch master
nothing to commit, working directory clean

这说明你现在的工作目录相当干净。换句话说,所有已跟踪文件在上次提交后都未被更改过。 此外,上面的信息还表明,当前目录下没有出现任何处于未跟踪状态的新文件,否则 Git 会在这里列出来。 最后,该命令还显示了当前所在分支,并告诉你这个分支同远程服务器上对应的分支没有偏离。 现在,分支名是 “master”,这是默认的分支名。

现在,让我们在项目下创建一个新的 README 文件。 如果之前并不存在这个文件,使用 git status 命令,你将看到一个新的未跟踪文件:

1
2
3
4
5
6
7
8
9
$ echo 'My Project' > README
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)

README

nothing added to commit but untracked files present (use "git add" to track)

在状态报告中可以看到新建的 README 文件出现在 Untracked files 下面。 未跟踪的文件意味着 Git 在之前的快照(提交)中没有这些文件;Git 不会自动将之纳入跟踪范围,除非你明明白白地告诉它“我需要跟踪该文件”, 这样的处理让你不必担心将生成的二进制文件或其它不想被跟踪的文件包含进来。 不过现在的例子中,我们确实想要跟踪管理 README 这个文件。

1
2
使用命令 git add 开始跟踪一个文件。 所以,要跟踪 README 文件,运行:
$ git add README

这时候我们在运行git status查看状态:

1
2
3
4
5
6
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: README

文件已经处于被追踪的状态了,并处于暂存状态。

现在我们来修改一个已被跟踪的文件。 如果你修改了一个名为 CONTRIBUTING.md 的已被跟踪的文件,然后运行 git status 命令,会看到下面内容:

1
2
3
4
5
6
7
8
9
10
11
12
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: README

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

modified: CONTRIBUTING.md

文件 CONTRIBUTING.md 出现在 Changes not staged for commit 这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。 要暂存这次更新,需要运行 git add命令。

git add:

这是个多功能命令:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。 将这个命令理解为“添加内容到下一次提交中”而不是“将一个文件添加到项目中”要更加合适。 现在让我们运行 git add 将”CONTRIBUTING.md”放到暂存区,然后再看看 git status 的输出:

1
2
3
4
5
6
7
8
$ git add CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: README
modified: CONTRIBUTING.md

现在两个文件都已暂存,下次提交时就会一并记录到仓库。 假设此时,你想要在 CONTRIBUTING.md 里再加条注释, 重新编辑存盘后,准备好提交。 不过且慢,再运行 git status 看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ vim CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: README
modified: CONTRIBUTING.md

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

modified: CONTRIBUTING.md

怎么回事? 现在 CONTRIBUTING.md 文件同时出现在暂存区和非暂存区。 这怎么可能呢? 好吧,实际上 Git 只不过暂存了你运行 git add 命令时的版本, 如果你现在提交,CONTRIBUTING.md 的版本是你最后一次运行 git add 命令时的那个版本,而不是你运行 git commit 时,在工作目录中的当前版本。 所以,运行了 git add 之后又作了修订的文件,需要重新运行 git add 把最新版本重新暂存起来:

1
2
3
4
5
6
7
8
$ git add CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: README
modified: CONTRIBUTING.md

git status

git status 命令的输出十分详细,但其用语有些繁琐。 如果你使用 git status -s 命令或 git status –short 命令,你将得到一种更为紧凑的格式输出。 运行 git status -s ,状态报告输出如下:

1
2
3
4
5
6
$ git status -s
M README
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt

新添加的未跟踪文件前面有 ?? 标记,新添加到暂存区中的文件前面有 A 标记,修改过的文件前面有 M 标记。 你可能注意到了 M 有两个可以出现的位置,出现在右边的 M 表示该文件被修改了但是还没放入暂存区,出现在靠左边的 M 表示该文件被修改了并放入了暂存区。 例如,上面的状态报告显示: README 文件在工作区被修改了但是还没有将修改后的文件放入暂存区,lib/simplegit.rb 文件被修改了并将修改后的文件放入了暂存区。 而 Rakefile 在工作区被修改并提交到暂存区后又在工作区中被修改了,所以在暂存区和工作区都有该文件被修改了的记录。

Redis和Memcached比较

  • Memcached是多线程,而Redis使用单线程.
  • Memcached使用预分配的内存池的方式,Redis使用现场申请内存的方式来存储数据,并且可以配置虚拟内存。
  • Redis可以实现持久化,主从复制,实现故障恢复。
  • Memcached只是简单的key与value,但是Redis支持数据类型比较多。

Redis的存储分为内存存储、磁盘存储 .从这一点,也说明了Redis与Memcached是有区别的。Redis 与Memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改 操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis有两种存储方式(默认:snapshot)

  1. snapshot

实现方法是定时将内存的快照(snapshot)持久化到硬盘,
这种方法缺点是持久化之后如果出现crash则会丢失一段数据。
因此在完美主义者的推动下作者增加了aof方式。

  1. aof

即append only mode,在写入内存数据的同时将操作命令保存到日志文件,在一个并发更改上万的系统中,命令日志是一个非常庞大的数据,管理维护成本非常高,恢复重建时间会非常长,这样导致失去aof高可用性本意。另外更重要的是Redis是一个内存数据结构模型,所有的优势都是建立在对内存复杂数据结构高效的原子操作上,这样就看出aof是一个非常不协调的部分。其实aof目的主要是数据可靠性及高可用性.

Redis

Redis是一个开源的,使用C语言编写,面向“键/值”对类型数据的分布式NoSQL数据库系统,特点是高性能,持久存储,适应高并发的应用场景。Redis纯粹为应用而产生,它是一个高性能的key-value数据库,并且提供了多种语言的API,性能测试结果表示SET操作每秒钟可达110000次,GET操作每秒81000次(当然不同的服务器配置性能不同)。

Redis目前提供五种数据类型:

  • string(字符串),
  • list(链表),
  • Hash(哈希),
  • set(集合),
  • zset(sorted set) (有序集合)

Redis开发维护很活跃,虽然它是一个Key-Value数据库存储系统,但它本身支持MQ功能,所以完全可以当做一个轻量级的队列服务来使用。

Redis可以做消息队列?

首先,redis设计用来做缓存的,但是由于它自身的某种特性使得它可以用来做消息队列,它有几个阻塞式的API可以使用,正是这些阻塞式的API让其有能力做消息队列;另外,做消息队列的其他特性例如FIFO(先入先出)也很容易实现,只需要一个list对象从头取数据,从尾部塞数据即可;redis能做消息队列还得益于其list对象blpop brpop接口以及Pub/Sub(发布/订阅)的某些接口,它们都是阻塞版的,所以可以用来做消息队列。

对于RabbitMQ和Redis的入队和出队操作,各执行100万次,每10万次记录一次执行时间。测试数据分为128Bytes、512Bytes、1K和10K四个不同大小的数据。
实验表明:

入队时,当数据比较小时Redis的性能要高于RabbitMQ,而如果数据大小超过了10K,Redis则慢的无法忍
受;出队时,无论数据大小,Redis都表现出非常好的性能,而RabbitMQ的出队性能则远低于Redis。

参考链接

String 的修改原理

String的修改其实就是new了一个StringBuilder并调用append方法,然后在调用toString返回一个新的String。(注意:append方法并不会new一个新的对象)

String类的了解

查看String源码,发现String类是被final修饰的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
{
/** The value is used for character storage. */
private final char value[];

/** The offset is the first index of the storage that is used. */
private final int offset;

/** The count is the number of characters in the String. */
private final int count;

/** Cache the hash code for the string */
private int hash; // Default to 0

/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;

......

}

 从上面可以看出几点:

  1)String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。在Java中,被final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法。

​ 2) 从上面的三个方法可以看出,无论是sub操、concat还是replace操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。

深入理解String StringBuilder StringBuffer

  1. String str=”hello world”和String str=new String(“hello world”)的区别

    JVM内存机制:在class文件中有一部分来存储编译期间生成的字面常量和符号引用,这部分叫做class文件的常量池。在运行期间对应着方法区运行时的常量池。

     因此在上述代码中,String str1 = “hello world”;和String str3 = “hello world”; 都在编译期间生成了 字面常量和符号引用,运行期间字面常量”hello world”被存储在运行时常量池(当然只保存了一份)。通过这种方式来将String对象跟引用绑定的话,JVM执行引擎会先在运行时常量池查找是否存在相同的字面常量,如果存在,则直接将引用指向已经存在的字面常量;否则在运行时常量池开辟一个空间来存储该字面常量,并将引用指向该字面常量。

      总所周知,通过new关键字来生成对象是在堆区进行的,而在堆区进行对象生成的过程是不会去检测该对象是否已经存在的。因此通过new来创建对象,创建出的一定是不同的对象,即使字符串的内容是相同的

    2.String StringBuilder StringBuffer的区别:

    String的修改其实就是new了一个StringBuilder并调用append方法,然后在调用toString返回一个新的String。

     那么有人会问既然有了StringBuilder类,为什么还需要StringBuffer类?查看源代码便一目了然,事实上,StringBuilder和StringBuffer类拥有的成员属性以及成员方法基本相同,区别是StringBuffer类的成员方法前面多了一个关键字:synchronized,不用多说,这个关键字是在多线程访问时起到安全保护作用的,也就是说StringBuffer是线程安全的。

     1)对于直接相加字符串,效率很高,因为在编译器便确定了它的值,也就是说形如”I”+”love”+”java”; 的字符串相加,在编译期间便被优化成了”Ilovejava”。这个可以用javap -c命令反编译生成的class文件进行验证。

      对于间接相加(即包含字符串引用),形如s1+s2+s3; 效率要比直接相加低,因为在编译器不会对引用变量进行优化。

    2)String、StringBuilder、StringBuffer三者的执行效率:

      StringBuilder > StringBuffer > String

     当然这个是相对的,不一定在所有情况下都是这样。

      比如String str = “hello”+ “world”的效率就比 StringBuilder st = new StringBuilder().append(“hello”).append(“world”)要高。

      因此,这三个类是各有利弊,应当根据不同的情况来进行选择使用:

      当字符串相加操作或者改动较少的情况下,建议使用 String str=”hello”这种形式;

      当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。

常见的关于String、StringBuffer的面试题

下面是一些常见的关于String、StringBuffer的一些面试笔试题,若有不正之处,请谅解和批评指正。

  1. 下面这段代码的输出结果是什么?

  String a = “hello2”;   String b = “hello” + 2;   System.out.println((a == b));

  输出结果为:true。原因很简单,”hello”+2在编译期间就已经被优化成”hello2”,因此在运行期间,变量a和变量b指向的是同一个对象。

2.下面这段代码的输出结果是什么?

  String a = “hello2”;   String b = “hello”; String c = b + 2; System.out.println((a == c));

  输出结果为:false。由于有符号引用的存在,所以 String c = b + 2;不会在编译期间被优化,不会把b+2当做字面常量来处理的,因此这种方式生成的对象事实上是保存在堆上的。因此a和c指向的并不是同一个对象.

3.下面这段代码的输出结果是什么?

  String a = “hello2”;   final String b = “hello”; String c = b + 2; System.out.println((a == c));

  输出结果为:true。对于被final修饰的变量,会在class文件常量池中保存一个副本,也就是说不会通过连接而进行访问,对final变量的访问在编译期间都会直接被替代为真实的值。那么String c = b + 2;在编译期间就会被优化成:String c = “hello” + 2;

4.下面这段代码输出结果为:

1
`public` `class` `Main {``    ``public` `static` `void` `main(String[] args) {``        ``String a = ``"hello2"``;``        ``final` `String b = getHello();``        ``String c = b + ``2``;``        ``System.out.println((a == c));``    ``}``    ` `    ``public` `static` `String getHello() {``        ``return` `"hello"``;``    ``}``}`

  输出结果为false。这里面虽然将b用final修饰了,但是由于其赋值是通过方法调用返回的,那么它的值只能在运行期间确定,因此a和c指向的不是同一个对象。

5.下面这段代码的输出结果是什么?

1
`public` `class` `Main {``    ``public` `static` `void` `main(String[] args) {``        ``String a = ``"hello"``;``        ``String b =  ``new` `String(``"hello"``);``        ``String c =  ``new` `String(``"hello"``);``        ``String d = b.intern();``        ` `        ``System.out.println(a==b);``        ``System.out.println(b==c);``        ``System.out.println(b==d);``        ``System.out.println(a==d);``    ``}``}`

  输出结果为(JDK版本 JDK6):

  img

  这里面涉及到的是String.intern方法的使用。在String类中,intern方法是一个本地方法,在JAVA SE6之前,intern方法会在运行时常量池中查找是否存在内容相同的字符串,如果存在则返回指向该字符串的引用,如果不存在,则会将该字符串入池,并返回一个指向该字符串的引用。因此,a和d指向的是同一个对象。

7.下面这段代码1)和2)的区别是什么?

1
`public` `class` `Main {``    ``public` `static` `void` `main(String[] args) {``        ``String str1 = ``"I"``;``        ``//str1 += "love"+"java";        1)``        ``str1 = str1+``"love"``+``"java"``;      ``//2)``        ` `    ``}``}`

  1)的效率比2)的效率要高,1)中的”love”+”java”在编译期间会被优化成”lovejava”,而2)中的不会被优化

什么是单例模式

因程序需要,有时我们只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计。

特点

  1. 单例模式只能有一个实例。
  2. 单例类必须创建自己的唯一实例。
  3. 单例类必须向其他对象提供这一实例。

单例模式VS静态类

在知道了什么是单例模式后,我想你一定会想到静态类,“既然只使用一个对象,为何不干脆使用静态类?”,这里我会将单例模式和静态类进行一个比较。

  1. 单例可以继承和被继承,方法可以被override,而静态方法不可以。
  2. 静态方法中产生的对象会在执行后被释放,进而被GC清理,不会一直存在于内存中。
  3. 静态类会在第一次运行时初始化,单例模式可以有其他的选择,即可以延迟加载。
  4. 基于2, 3条,由于单例对象往往存在于DAO层(例如sessionFactory),如果反复的初始化和释放,则会占用很多资源,而使用单例模式将其常驻于内存可以更加节约资源。
  5. 静态方法有更高的访问效率。
  6. 单例模式很容易被测试。

几个关于静态类的误解:

  • 误解一:静态方法常驻内存而实例方法不是。

实际上,特殊编写的实例方法可以常驻内存,而静态方法需要不断初始化和释放。

  • 误解二:静态方法在堆(heap)上,实例方法在栈(stack)上。

实际上,都是加载到特殊的不可写的代码内存区域中。

静态类和单例模式情景的选择:

情景一:不需要维持任何状态,仅仅用于全局访问,此时更适合使用静态类。

情景二:需要维持一些特定的状态,此时更适合使用单例模式。

单例模式的实现

1. 懒汉模式(线程不安全)

1
2
3
4
5
6
7
8
9
10
11
12
public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo(){

}
public static SingletonDemo getInstance(){
if(instance==null){
instance=new SingletonDemo();
}
return instance;
}
}

如上,通过提供一个静态的对象instance,利用private权限的构造方法和getInstance()方法来给予访问者一个单例。

缺点是,没有考虑到线程安全,可能存在多个访问者同时访问,并同时构造了多个对象的问题。

之所以叫做懒汉模式,主要是因为此种方法可以非常明显的lazy loading。

针对懒汉模式线程不安全的问题,我们自然想到了,在getInstance()方法前加锁,于是就有了第二种实现。

2. 懒汉模式二 (线程安全)

1
2
3
4
5
6
7
8
9
10
11
12
public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo(){

}
public static synchronized SingletonDemo getInstance(){
if(instance == null){
instance = new SingletonDemo();
}
return instance;
}
}

然而并发其实是一种特殊情况,大多时候这个锁占用的额外资源都浪费了,这种打补丁方式写出来的结构效率很低。

3. 饿汉模式

1
2
3
4
5
6
7
8
9
public class SingletonDemo {
private static SingletonDemo instance = new SingletonDemo();
private SingletonDemo(){

}
public static SingletonDemo getInstance(){
return instance;
}
}

直接在运行这个类的时候进行一次loading,之后直接访问。显然,这种方法没有起到lazy loading的效果,考虑到前面提到的和静态类的对比,这种方法只比静态类多了一个内存常驻而已。

4. 静态类内部加载

1
2
3
4
5
6
7
8
9
10
11
public class SingletonDemo {
private static class SingletonHolder{
private static SingletonDemo instance = new SingletonDemo();
}
private SingletonDemo(){
System.out.println("Singleton has loaded");
}
public static SingletonDemo getInstance(){
return SingletonHolder.instance;
}
}

使用内部类的好处是,静态内部类不会在单例加载时就加载,而是在调用getInstance()方法时才进行加载,达到了类似懒汉模式的效果,而这种方法又是线程安全的。

5. 枚举方法

1
2
3
4
5
6
7
8
9
10
11
12
enum SingletonDemo{
INSTANCE;
public void otherMethods(){
System.out.println("Something");
}
}

public class Hello {
public static void main(String[] args){
SingletonDemo.INSTANCE.otherMethods();
}
}

提倡的方式,在我看来简直是来自神的写法。解决了以下三个问题:

  • (1)自由序列化。
  • (2)保证只有一个实例。
  • (3)线程安全。

这种充满美感的代码真的已经终结了其他一切实现方法了。

6. 双重校验锁法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo(){
System.out.println("Singleton has loaded");
}
public static SingletonDemo getInstance(){
if(instance == null){
synchronized (SingletonDemo.class){
if(instance == null){
instance = new SingletonDemo();
}
}
}
return instance;
}
}

接下来我解释一下在并发时,双重校验锁法会有怎样的情景:

STEP 1. 线程A访问getInstance()方法,因为单例还没有实例化,所以进入了锁定块。

STEP 2. 线程B访问getInstance()方法,因为单例还没有实例化,得以访问接下来代码块,而接下来代码块已经被线程1锁定。

STEP 3. 线程A进入下一判断,因为单例还没有实例化,所以进行单例实例化,成功实例化后退出代码块,解除锁定。

STEP 4. 线程B进入接下来代码块,锁定线程,进入下一判断,因为已经实例化,退出代码块,解除锁定。

STEP 5. 线程A初始化并获取到了单例实例并返回,线程B获取了在线程A中初始化的单例。

理论上双重校验锁法是线程安全的,并且,这种方法实现了lazyloading。

什么是线程?

是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。线程有就绪、阻塞和运行三种基本状态。

什么是进程?

进程是计算机中的程序关于某个数据集合的一次活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。一个可以独立运行的程序单位。是线程的集合。

什么是多线程

同一个进程中有多个线程在并发执行。进程的多个执行路径。

进程和线程之间的区别

线程是进程的最小运行单位,进程是线程的集合。一个进程由一个或多个线程组成。

线程的五种状态?—生命周期

创建(new) 就绪(runnable) 运行(running) 阻塞(blocked) 死亡(dead)

img

为什么要用多线程?

单进程会出现阻塞,这时整个进程就会挂掉,直到外部条件发生变化。

1.异步调用: 读写分离,IO操作等。如果是单线程,那么程序就必须等待这些操作执行完成之后才能执行其他的操作。使用多线程,可以将耗时任务放在后台继续执行的同时,执行其他任务。

2.可以提高程序效率。

缺点:

1.多线程消耗系统资源,因为线程需要开辟内存。多线程就需要更多的线程。

2.需要考虑线程操作对程序的影响,如线程挂起,中止等。

3.线程使用不当会发生很多问题。

多线程是异步的,但不代表多线程真的是几个线程是在同时进行,实际上是系统不断地在各个线程之间切换(由于切换速度很快,所以给我们在同时运行的错觉)

实现多线程的方式

1.继承Thread()类,重写run()方法

Thread()类本质上是实现了Runnable接口的一个实例,代表一个线程实例。启动线程的唯一方法就是通过Thread()类的start()方法。start是一个native方法,它将启动一个线程,并执行run()方法。这种实现多线程很简单,通过自己的类extends Thread,并复写run()方法,就可以启动并执行自己定义的run()方法。例如:

1
2
3
4
5
6
7
8
9
10
public class MyThread extends Thread {  
  public void run() {
   System.out.println("MyThread.run()");
  }
}

MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start();

start()方法本质是将线程进入就绪状态,等待cpu执行run()方法,如果直接调用thread.run方法,那么就和普通的方法没有区别,不是异步操作。

2.实现Runnable接口创建线程

如果自己的类已经继承其他的类,就可以实现Runnable接口,java不支持多继承。

1
2
3
4
5
6
7
8
class MyRunable extends Runnable {  
  public void run() {
   System.out.println("Runnable.run()");
  }
}
//启动实现runnable接口创建的线程对象
Thread myThread1 = new Thread(new MyRunable());
myThread1.start();

Runable 避免了java单继承的局限性;更加符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。

Runable只是任务,只是定义了线程要执行的任务,Thread才是真正的线程创建者,也就是说线程和任务是分开的,任务放在放在线程里面才会被执行。run()方法是线程的运行者,main只有调用了run()方法才能执行任务。

strat()方法是创建线程。

总结:1.start()是线程的启动者;2.run()是线程运行的执行者。 总结:1.实现Runnable接口的实例是线程任务的定义者;2.继承Thread类的实例是线程的创建者

线程的创建,停止,常用方法介绍:

停止的3种方法:

1.线程正常停止,即run()方法运行结束正常停止。

2.interrupt()方法终止线程。

3.stop()方法暴力终止。

 interrupt方法中断线程介绍:

    interrupt方法其实并不是直接中断线程,只是给线程添加一个中断标志。

  判断线程是否是停止状态:

    this.interrupted(); 判断当前线程是否已经中断。(判断的是这个方法所在的代码对应的线程,而不是调用对象对应的线程)

    this.isInterrupted(); 判断线程是否已经中断。(谁调用,判断谁)

什么是线程安全

当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。

守护线程:

java线程分为两种:

1.用户线程,运行在前台,执行具体的任务

2.守护线程,运行在后台,为前台线程服务(GC)

特点:一旦所有的用户线程都结束运行,守护线程会随着JVM一起结束工作。垃圾回收线程就是一个常见的守护线程。

Start()和run()方法

1.start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。
2.run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。

简介

GC garbage collection 垃圾收集,垃圾回收机制。

java中的GC回收完全是自动的,没有提供相应的api手动回收,所有的内存分配和回收权限都在jvm,不再开发人员手里。

没有绝对的垃圾回收的方法,不过可以这样去做:

1.对于不再引用的对象,及时把它的引用赋为null. obj = null

2.如果内存确实很紧张,调用System.gc()方法来建议垃圾回收器开始回收垃圾。通知GC运行,但是java规范并不一定保证GC一定执行。

守护进程

线程分为守护线程和非守护线程(即用户线程)

只要当前jvm实例中尚存在任何一个 非守护线程没有结束,守护线程就全部工程。只有当最后一个非收获线程结束时,守护线程随着JVM一同结束工作。守护线程最典型的就是GC(垃圾回收器)

native 关键字

native是用做java和其他语言进行(如C++)进行协作时用的,也就是native后的函数的实现不是用java写的。