java基础

java概述

一、数据类型、数组

1.1 java基本数据类型

1.1.1 整型_基本数据类型

		byteB2*8次幂   -128——127
		short2B) 2*16次幂   -32768——32767
		int4B) 2*32次幂  -2147483648——2147483647
		long8B) 3*64次幂  -2*63——2*63次幂 

1.1.2 非整型_基本数据类型

		 boolean1B) 字面值  true/false  主要用于逻辑判断
		 char2B) 	表示一个字符,采用Unicode编码方式(与int相通)
	 	 float       (4B) 数据默认为double 字面值 定义时为    float  f=1.4F
		 double     ( 8B)

1.1.3 数据类型的转换

	1、自动类型提升:目标类型大于源类型    byte b=20  short s=b;  b数据类型自动提升
				a+b:ab中有double ——结果为double
				a+b:ab中有float——结果为float
				a+b:ab中有Long——结果为Long
			    a+b:ab中以上都不满足——结果为int
	2、强制类型转换:目标类型小于源类型   short s=10     byte  b=(byte)s;
	   
	   字符串只能用于加法运算——表示拼接   如:“A+B=AB
		   char数据类型可用于运算                          如:'A'+'B'=131
		   boolean 类型不能和其他类型做转换
		   任何类型的数据与字符串String类型的数据相加,类型自动提升为字符串类型    
			     列如:10+0+20+50+1= 78501      结果为字符串类型

1.1.4 标识符命名

			  标识符命名语法要求:
				1、组成要求:     必须是   字母、数字、_ 、$ 四种字符组成 ——数字不能开头
				2、大小写敏感
				3、不能使用java中的关键字和保留字   如:class 、 pakeage等
				4、没有长度限制
			 习惯
				   1、望文生义
				    2、大小写
						包名:全小写
						类名:每个单词首字母大写
						变量名、函数名、方法名’:首字母小写、后面单词首字母大写
						常量名;全大写
		   三目运算符语法:
				   布尔表达式?结果1:结果2   
				    int result=a>b?3:2  

1.1.5 位运算

  • 十进制8转二进制及二进制转十进制
    在这里插入图片描述
    1000(二进制)=0*2^0+0*2^1+0*2^2+1*2^3=8(十进制)
                  =0+0+0+8=8
  • ^(亦或)
  相同的为0,不同的为1
  2^31 0010^0011——0001  
  • &(与)
 只要有一个为0,就为0   
 2&32   0010&00110010 
  • ~ 按位取反(单目运算符)
1就变成00就变成1
  • <<(向左位移)
   针对二进制,向左移动3位,后面用0补齐  
		2<<3——16   0010   0001 0000——1*2*2*2*2——16
  • >>(向右位移)
 针对二进制,转换成二进制后向右移动32>>30   0010
  • >>>(无符号右移)
	无符号右移,忽略符号位,空位都以0补齐
	二进制,十进制,十六进制的转换
	  十进制转换二进制:十进制数除以二,从上到下——从左到右,
	  二进制转换为十进制 :
	   			二进制的右边第一个数开始,每一个乘以2的n次方,n从0开始,每次递增1。
	   			然后得出来的每个数相加即是十进制数
	   			
	   十进制转换为十六进制:十进制数除以16  125/16=137
	   十六进制转换为十进制:
	   		十六进制数的右边第一个数开始,每一个乘以16的n次方,n从0开始,
	   		 每次递增1。然后得出来的每个数相加即是十进制数

			应用在有返回值的函数中时,结束函数返回结果
			应用在没有返回值的函数中时,表示跳出当前函数,回到函数的调用位置
			  			

1.1.6 return的注意事项

	应用在有返回值的函数中时,结束函数返回结果
	应用在没有返回值的函数中时,表示跳出当前函数,回到函数的调用位置		  			

1.2 数组的排序

1.2 .1 数组定义

数组定义的方式一:
	1、定义、int[] a;
	2、分配空间、a=new int[3]
	3、为数组赋值数组元素  a[0]=1   a[1]=3  a[2]=5
数组定义方式二
	数组的初始化显示   int[] a={1,   2   ,3   , 2};
数组的定义方式三
	int[] a=new int[]{1,2,3,2}

1.2 .2 冒泡排序

		步骤1:外层循环    int i=1;i<a.length;i++
		步骤2:内层循环   int j=0;j<a.length-1;j++
		步骤3;条件,如果  a[j]>a[j+1]
		步骤4;循环体中通过引入     新变量    交换a[j] 和a[j+1] 的位置

		 public static void main(String[] args) {
		        int[] array={1,6 ,5 ,2};
		        for (int i = 1; i < array.length; i++) {
		            for (int j = 0; j < array.length-1; j++) {
		                if(array[j]>array[j+1]){
		                    int tem=array[j+1];
		                    array[j+1]=array[j];
		                    array[j]=tem;
		                }
		            }
		        }
		        for (int i = 0; i < array.length; i++) {
		            System.out.println(array[i]);
		        }
		    }

1.2 .3 选择排序

		1、外层循环  int i=0;  i<a.length-1;   i++
		2、内层循环  int j=i+1;  j<a.length ;    j++
		3、判断条件 a[i]>a[j]
		4、交换位置
		
		    public static void main(String[] args) {
		        int[] array={1,6 ,5 ,2};
		        for (int i = 0; i < array.length-1; i++) {
		            for (int j = i+1; j < array.length; j++) {
		                if(array[i]>array[j]){
		                    int tem=array[j];
		                    array[j]=array[i];
		                    array[i]=tem;
		                }
		            }
		        }
		        for (int i = 0; i < array.length; i++) {
		            System.out.println(array[i]);
		        }
		    }
				1
				2
				5
				6

1.2 .4 快速排序

	     java. util . Arrays.sort  ( a );    默认升序

二、面向对象、继承、封装、多态、接口

2.1 面向对象

2.1.1 概念: 客观事物,Java程序中的一种表现形式.
2.1.2 对象的组成:
			对象的属性:——描述对象有什么特征.
			对象的方法:——描述对象有什么功能.
2.1.3:计算机中—用类描述对象的属性和方法
		成员变量
			1、位置:类以内,方法以外
			2、成员变量具有默认值,默认值同数组:
			3、作用范围至少在本类中有效
			4、类中不能出现重名的成员变量,但是成员变量可以和局部变量命名冲突, 
				 成员变量和局部变量命名冲突时,局部变量优先被访问.
		局部变量
			1、位置 :定义在函数内部的变量.
			2、先赋值 后使用;
			3、作用范围:从定义位置开始,到定义它的代码块结束;
			4、 在 重合的作用范围内,不允许定义相同名字的变量 (避免命名冲突).
        属性: 描述对象有哪些特征  (保留程序功能所关注的属性)
		方法:描述对象有哪些功能
			位置: 定义在类 以内,其它方法以外.
			方法的定义分为
					方法声明: 代表当前对象 能做什么
					方法的实现:代表如何实现对应功能(如何做)
		构造方法: 是一种特殊的方法.
			a 特点:
				 I. 构造方法的方法名必须和类名完全一致;
				 II. 构造方法没有返回值类型(void 都没有)
				 III. 构造方法允许重载
				 IV. 构造方法不允许手工的调用
			b、作用
				 I. 用于创建对象
				 II. 通常给属性赋值
			注意
				I. 构造方法不能手动的调用,在创建对象时,JVM自动调用1.
				II. 如果在一个类中没有定义任何构造方法,则JVM会自动添加一个默认 
					公开的/无参数的构造法方法
				iii:如果在类中定义了有参数的构造方法,则系统不在提供默认公开的/无
					参数的构造法方法
2.1.4 成员变量和局部变量的区别?——定义位置、作用范围:、默认值、命名冲突:
2.1.5 类和对象的关系:
			类是对象的模板
			对象是类的实例
	2.1.6 方法的重载概念;
		在一个类中定义 多个同名的方法,但是参数列表不同
			I. 方法名相同
			II. 参数列表不同(类型 / 个数 / 顺序)
			III. 修饰符/返回值类型/异常没有要求.
		使用:如果程序中应用了方法的重载,会根据调用时传递的实际参数,
		 注意: 根据调用时传递的实际参数,先精确匹配,如果没有,则 就近向上匹配,但是
		 避免发生匹配混淆.
	2.1.7 对象创建的过程
		1. 分配空间: 给所有的属性赋默认值
		2. 初始化属性: 给属性赋初始值(给属性第二次赋值机会)
		3. 调用构造方法:给属性赋值(给属性第三次赋值的机会
	2.1.8 this关键字
		(1)、当实例变量与局部变量重名,优先访问局部变量 想要访问实例变量需要
				     使用this.实例变量进行区分
		(2)、在类中使用this.方法名();表示访问当前类中的其他成员方法
		(3)、在一个类的构造方法中调用本类其他构造方法 语法:this(实参列表);
		      注意:使用this调用其他构造方法,this必须放在构造方法中的第一行.
	2.1.9 引用
		1. 引用类型的变量存储对象在堆空间的首地址.
		2. 每一个对象在堆空间相互独立
		3. 引用类型的变量之间相互赋值,传递的是对象在堆空间的首地址
				——基本类型的变量之间相互赋值,传递的是数值
		4. 用 null 调用属性或是方法时,编译通过,运行报错,错误信息

2.2 继承

2.2.1 概念: 
	继承体现的是类之间的一种 "is-a" 关系
	继承是一种机制,通过继承机制,可以让 子类 继承 父类中的属性和方法.
2.2..2 优点
	体现代码的 可复用性和可扩展性
2.2.3 方法的覆盖
	继承关系中,子类中定义了和父类中一样的方法-----使用时,子类中方法优先使用..
	要求:
		a. 子类的方法名/参数列表/返回值 和 父类一致
		b. 子类的访问修饰符和父类相同或是比父类宽
2.2.4 使用场景:
	  当父类中方法不足于满足子类的需求时,子类覆盖父类中的方法
2.2.5 子类可以继承父类的哪些内容
	① 构造方法不能被继承
		a. 构造方法的方法名 必须和类名相同
		b. 子类中属性和方法比父类中的多,子类的对象创建内容比父类复杂则子类必须编写自己
	   		的构造方法,完成对象的创建
	② 属性 和 成员方法取决于 访问修饰符
		private(私有的  不能被继承
		default(默认的) 同包子类可以继承
		protected(受保护的)  可以被继承
		public(公开的/公共的)  可以被继承
2.2.6 创建对象的过程
	① 分配空间(子类+父类):给所有的属性赋默认值
	② 递归的构造父类对象
			 a. 初始化父类的属性
			 b. 调用父类的构造方法
	③ 初始化本类的属性
	④ 调用本类的构造方法
2.2.7 super用法
	1>super.方法():调用父类中的方法
	2>super.属性:调用父类中的属性
	
	3>super():调用父类的无参构造
	4>super(实参):表示调用父类的带参构造
		注意: 
			1>继承关系下创建子类对象会优先创建父类对象
			2>子类中每一个构造方法如果没有显示书写super()或者super(实参)或者this()
				或者this(实参), 那么首行,隐式存在super()
		    3>在同一个子类构造中this()super()不能同时存在
		       

2.3 封装

封装概念
		① 所有的属性私有  ,private 修饰
		② 为私有的属性提供公开的get/set方法
作用:
		保证数据安全

2.4 多态

	2.4.1 概念
		① 多态:父类型的引用 指向 子类型的对象
		② 如果用父类型的引用调用属性或是方法,只能调用父类中声明属性和方法
		③ 如果子类覆盖了父类中的方法,则运行时会执行子类覆盖后的方法 否则,直接
			执行父类中的方法.
	2.4.2 引用之间的转换
		① 子类型的 引用赋值给 父类型引用 --->直接赋值
		② 父类型的 引用赋值给 子类型的引用--->强制类型转换
		如何避免 类型转换异常 ?
			a.语法: 引用名 instanceof 类名
			b.作用: 判断引用中存储的对象类型 和 后面的类型是否
			        兼容,兼容-true; 不兼容-false.
			c. 应用:在类型转换时,可以先通过 instanceof 判断,
			       可以避免类型转换异常(java.lang.ClassCastException
		③ 	如果转换的双方不存在继承关系,不允许转换,编译报错
	2.4.3 多态应用
		① 多态用在数组上:本类型+所有的子类型 都可作为数组元素内容
		② 多态用在形参上:本类型+所有的子类型 都可以作为实际参数传递
		③ 多态用在返回值上:本类型+所有的子类型 都可以作为返回值返回
	2.4.4. 多态好处:
		多态屏蔽子类之间的差异,对子类进行统一操作	

2.5 三大修饰符

	2.5.1abstract (抽象的)
		 abstract 修饰的类称为 抽象类
			① 抽象类不能单独创建对象,但是可以声明引用.
			② 抽象类中有构造方法,用于创建子类对象先去创建父类时,供子类调用
		abstract 修饰的方法称为 抽象方法.
			抽象方法只有方法的声明,没有方法的实现.({}都没有)
			抽象方法只能定义在抽象类中  抽象类中可以定义非抽象的方法.
			注意: 子类继承抽象类,如果子类不想成为抽象类,则必须实现(覆盖)抽象类中的      所有的抽象方法.否则,子类也必须为抽象类.
			抽象类型强制使用多态.
	2.5.2static(静态的)
		静态属性/类变量/静态变量
			语法: 访问修饰符 static 数据类型 属性名; 
			特点: 类变量,是全类共有属性,与创建对象的多少无关
			使用: 类名.静态属性名              对象名.静态属性名 
		静态方法
			语法: 访问修饰符 static 返回值类型 方法名(形参列表){}
			使用: 类名.静态方法名(实参);-         对象名.静态方法名(实参)
			注意
				静态方法中不能直接 访问本类的非静态成员(非静态的属性和方法);
				 静态方法中可以直接访问本类的静态成员(静态属性和静态方法);
				静态方法中不能使用 this/super 关键字
				静态方法可以被继承, 只能被 static 方法覆盖,但是没有多态.
		静态初始化代码块
			初始化代码块(动态代码块)[了解]
				a.位置:定义在类以内,方法以外
				b.作用:在创建对象时,按照和属性定义的先后顺序,完成属性的初始化
			静态代码块 [重点
				a.位置: 定义在类以内,方法以外,static 修饰
				b. 语法:  static{}
				c. 作用: 在类加载的时候,按照 和 静态属性定义的先后顺序,完成静态属性的初始化工作.
			d. 类加载:
				概念: 当JVM 第一次使用某个类时,通过 ClassPath找到这个类对应的.class 文件,并对此.class文件进行读取,读取该类信息(类名/属性/包名/父类/方法等),读取之后保存在JVM内存中,只进行一次
				II. 类加载的时机:
					(1) 第一次创建一个类的对象时,先进行类加载,再完成对象的创建
					(2)第一次访问一个类中的静态成员(静态属性和静态方法),    会导致类进行类加载
					(3) 子类类加载 会先加载其父类
						第一次访问子类中的静态属性或是静态方法
						第一次创建子类的对象
	2.5.3final(最后的/最终的)
		1. final 可以修饰变量
			① final 修饰的变量(局部变量/实例变量/类变量)只允许一次赋值为作用范围内的常量.final 修饰的实例变量不再具有默认值
				初始化的机会:
					a. 声明的同时对其初始化
					b. 在构造方法中对其初始化,必须保证每一个构造方法   都要对其初始化
			③ final 修饰的类变量没有默认值.
				初始化的时机
				a. 声明的同时对其初始化
				b. 在静态代码块中对其初始化
				注意:如果 final修饰的变量数据类型为引用类型,则引用中存储的 对象地址不允许改变.
		2. final 修饰的方法
			修饰的方法可以被继承,但是不允许被覆盖,允许重载
		3final 修饰的类
			不允许被继承,即没有子类
			类 String/System/Math

2.6 接口

	2.6.1. 接口的概念
		是一种标准,程序中接口的使用者和接口的实现者,都必须遵循的约定
		① 关键字:interface
		② 不能创建对象,但是可以声明引用
		③ 接口编译之后,会生成 .class 文件
		④ 接口中没有构造方法
		⑤ 接口中所有属性都是 公开 静态 常量(默认 public static final 修饰)
		 ⑥ 接口中所有方法都是 公开 抽象方法(默认 public abstract 修饰)
	2.6.2. 接口的实现类
		1. 语法: class 类名 implements 接口名{}
		2. 注意
			实现类实现接口时,必须实现接口所有的方法否则实现类也需定义为抽象类
			接口中方法默认访问权限为 public,所以实现类实现接口中方法时,方法的权限
				必须为 public 
		3. 使用:
			父接口名 引用名 = new 实现类类名(实参);//强制使用多态
	2.6.3. 接口的继承关系
		1. 接口与接口之间是多继承:
			语法: interface 接口名 extends 父接口1,父接口2{}
		2. 一个类可以同时实现多个接口:类和接口是多实现的关系
			语法: class 类名 implements 接口名1,接口名2{}
		——注意:如果实现类不想成为抽象类,需实现所有接口 中所有方法
		3. 一个类可以实现多个接口,同时继承一个父类,但是必须是先继承后实现
		——语法: class 类名 extends 父类名 implements 接口名1,接口名2{}
	2.6.4. 接口多继承的影响
		1. 如果转换双方有一方为接口类型,不管双方有没有继承/实现关系,编译一定通过;
		如果实际存储的对象类型和要转换的类型相兼容,则运行通过;
		如果实际存储的对象类型和要转换的类型不兼容,则运行报错,错误信息:
		java.lang.ClassCastException(类型转换异常)
	2.6.5. 接口好处
		1. 扩充子类的能力
			① Java中的类是单继承,当父类中定义的方法不足以满足子类的功能范围需求时,
			可以实现接口扩充子类的能力
			② 通常将主要功能定义在父类中,次要的功能定义在接口中.
		2. 解耦合
			接口定义好之后,将接口的实现者和使用者进行分离,利用多态,降低各模块的耦合度
	2.6.6. 接口的回调
		接口的回调:接口定义好之后,先有接口的使用者,再有接口的实现者
		注意:接口回调的应用场景时,开发者关注的是根据接口        写出接口的实现部分
		

三、集合

3.1 Collection集合

 3.1.1 集合概念:是一种工具,一种容器,用于存储多个对象
		   熟悉(接口的特点、接口方法、接口的实现类、遍历)
 3.1.2  特点:存储Object类型 的 对象.
 3.1.3、接口常用的方法:
	a—— boolean add(Object o):  往集合中添加元素,添加成功-true;否则-false.[重点]
	b.——boolean contains(Object o):判断集合中是否包含o对象,包含-true;否则-false.
	c.——boolean remove(Object o):  从当前集合中将o对象删除.
	d.——int size():  获取集合中有效元素的个数.

3.2 list集合

3.2.1 特点:存Object,——有序、有下标、元素允许重复.
3.2.2ArrayList
	常用方法:
		a. boolean add(Object o): 添加元素——添加成功-true;否则-false.[重点]
		b. void add(int index,Object o):  将元素o对象插入到指定的位置.
		c. Object get(int index): 获取指定下标对应集合元素.
		d. Object remove(int index): 删除指定位置的集合元素,返回值为被删除的元素.
		f. Obejct set(int index,Object o): 修改指定位置对应的集合元素内容, 原始内容
			作为返回值返回(新值替换旧值
	 底层数组实现,查询效率高,增删慢;JDK1.2 版本,线程不安全,运行效率高。
3.2.3、verctor
	    数组实现,查询快,增删慢;  JDK1.0 版本, 线程安全,运行效率低。
3.2.4LinkedList
	  链表实现,查询慢,增删快。
3.2.5Stack 栈结构的经典实现,先进后出的数据结构。继承了 Vector,线程安全
	
	ArrayListVectorLinkedList的区别?

3.3 Set集合

3.3.1、特点:存Object象,无序、无下标、不允许重复。
3.3.2HashSet
	如何保证元素不重复
		底层依靠两个方法:hashCode()equals()1)、先执行hashCode(),判断哈希值是否相同
		(2)、哈希值不同(内容一定不一样), 存储到集合中
		(3)、哈希值相同(内容一定不一样),比较equals()
			内容相同:不存
			内容不同:存储
3.3.3 linkedSet
	  Set 集合的哈希实现,维护了元素插入顺序。
	  			
3.3.4TreeSet(可维护添加元素排列顺序)
	(1)、存储的对象是自定义类型
	(2)、实现Comparable接口,覆盖compareto方法,自定义排序规则
	(3)、返回值=0,不存储;  >0,放树的右侧 :<0 放树的左侧
		rerurn  this.age-o.age==0? this.name.compareTo(o.name)  :this.age-o.age
3.3.5、自定义对象
	如果使用Object类中的hashCode()equals(),往往内容相同的也会存储(地址不同)
	1、如果存储自定义对象,请在类中覆盖hashCode()equals()两个方法。
	2、覆盖hashCode() 方法  
		原则 :保证内容相同哈希值一定相同,内容不同尽量返回不同哈希值。
		 teturn  this.age + ((this.name == null) ? 0 : name.hashCode());
	3、覆盖eqeals()方法,   保证两个对象的内容相同,不存储
						自反性判断
						非空 判断
						获取getGlass()
						强转
						比较内容

3.4 MAP集合

3.4.1、特点:
	① 存储——键值对   (key-value)
	② 键:无序、无下标、元素不允许重复(唯一性)
	③ 值:无序、无下标、元素允许重复。
3.4.2、接口方法:
	Map集合常用方法:
		① V put(K key,V value):将一个键值对存储在map集合中,——如果键在map中
			不存在, 则直接存储,返回值为null——;如果键在map中已经存储在,则
			新的value数据覆盖原有的value数据,原有value数据,作为返回返回。  
		② V remove(K key):通过键从map中删除对应的键值对,被删除的值作为返回值返回。
		③ V get(K key) ——:通过键获取对应的值。
		④ boolean containsKey(K key):判断map集合中是否包含某个键,包含-true; 不包含-false.boolean containsValue(V value):判断集合中是否包含某一个值,
			包含-true;   不包含-false.int size():获取map中键值对个数。
		
3.4.3、实现类:
	1HashMap   JDK1.2版本, 线程不安全,运行效率高,允许null作为key/value.
	2、hashtable JDK1.0版本, 线程安全,  运行效率低,不允许null作为key/value.
	3Properties: 
			Hashtable的子类,键和值都要求为String类型。通常用于读取配置文件。
	4TreeMap
		SortedMap的实现类(SortedMapMap的子接口),对key完成自动排序。底层——红黑二叉树
	5LinkedHashMap:在 HashMap 的基础上,增加了对插入元素的链表维护。
	6WeakedHashMap:在 HashMap 的基础上,使强引用变为弱引用。	
 注意HashMapHashtable的区别?
 

3.5 集合的排序

3.4.1、方法一、利用工具类
	数组的排序:
		Arrays.sort(S);
	//静态工具,对    S  String 以及   int  数组  a 数组进行排序 
	//String类型的数组,快速的转换成一个list集合
		List<String> b=Arrays.asList(S);
	//将list 集合进行排序  用的工具为 Collections 静态方法 静态工具进行的排序
		Collections.sort(b);
3.4.2、自定义排序
	实现Compareble接口 ,覆盖compareTo方法
	定义排序规则
3.4.3、比较器排序
	(1)、自定义一个比较器 ,定义排序规则
		Comparator<Student131> c1 = new Comparator<Student131>() {
		public int compare(Student131 o1, Student131 o2) {
		return o1.age - o2.age;   }  }2)、传去一个集合和一个比较器,出入工具类   Collections.sort(list, c1); 

3.6 线程安全的集合

在这里插入图片描述

四、多线程

  • 并发原理:单核CPU在任何时间点上,只能运行一个进程,宏观上并行,微观上串行

4.1 进程的概念

1)、程序是静止的,只有真正运行的程序 ,才代被称为进程。
(2)、进程彼此完成不同的工作,交替执行。

4.2 线程的概念

1、即一个程序顺序在执行流程
	一个操作系统,多个任务,每一个任务都是一个进程,
	一个进程包含多个任务,每个任务都是一个线程。

2、进程和线程的区别
	多进程:堆空间是独立的,一个进程有自己的独立的堆空间,
	多线程:多个线程共享一个堆空间
	
	一个线程是一个流程,十个不同的线程就是十个不同的流程,局部变量跟流程相关,
		栈空间是线程独有的,每个线程都有每个线程自己的栈空间
		
	每当我们启动一个线程的时候,虚拟机都会为这个线程分配一个新的栈空间
		线程的栈帧,局部表量表都存在自己的栈空间中,线程与线程之间互不影响。	
	

4.3 线程的组成

1、cpu时间片:  
	操作系统分配
2、数据空间(堆、栈):
	一个数据要想被多线程共享,就作为成员变量,——反之作为局部变量
3、任务    
	形式体现为代码,一个线程启动,就要给线程分配任务,任务就体现在代码上

4.4 线程对象、线程区别

1、线程对象
	(1)、是java 的概念,创建线程对象,不代表创建线程
	(2)、线程对象是在堆空间里的
2、线程
	(1)、是操作统统的概念
	(2)、线程是在底层的操作系统中的,
	(3)、想让线程真正启动,可以通过操作线程对象来完成,如对t1掉start()方法,
		 这是对应的线程才会存在,对t1调start(),他就帮我从操作系统中,去开
		 创一个新线程,并让他启动

4.5 单线程、多线程

1、单线程
		程序只有一条执行路径
2、多线程:1)、程序至少有两条执行路径,主函数中的为主线程,
	(2)、当一个进程中,所有的线程都进入了终止状态,我们的虚拟机进程就会终止
	(3)、主线程结束,不代表进程结束,只有当所有的线程都进入了终止状态时,进程才会结束
3、守护线程 t1.setDaemon(true)
	 其他线程结束,不管本线程运行书否结束,进程都结束

4.6 实现线程的三种方式

  • 一个线程,主任务写在主函数(及入口函数)里,其他的任务,通过以下的三种方式实现

4.6.1 继承Thread类,重写其run()方法

1>自定义类继承Thread
	public class MyThread1 extends Thread {
2>重写run()方法
     @Override      
     public void run() {
     System.out.println("MyThread1.java")
     };
3>创建线程对象 Thread t1 = new MyThread1();
4>启动线程     t1.start();
class TaskB extends Thread{
	@Override
	public  void run(){
		for(int i=0;i<50;i++){
			System.out.println("&&&&"+i+"   "+getName());
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

4.6.2 实现Runnable接口,并实现其run()方法

	1>自定义——任务类实现Runnable
			public class Task1 implements Runnable {
	 2>覆盖run()方法
		    @Override      
		    public void run() { 
		     System.out.println("task1.java")
		     };
	3>创建任务对象 Task1 task = new Task1()
	4>创建线程对象 Thread t = new Thread(Task1);
	5>启动线程     t.start();
class TaskA implements Runnable{
	@Override
	public void run() {
		for(int i=0;i<100;i++){
			System.out.println("Hell"+"  "+i+"  "+Thread.currentThread().getName());					
			//主函数   有限期等待 Thread.sleep()
			//父类中没有抛异常,处理异常只能try...catch
				try {
					//每打印一个数之后 自定放弃 cpu 若干秒
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
		}
	}
}
public static void main(String[] args) throws InterruptedException {
		//	Thread类中方法:
		//		static void sleep(long millis):让当前线程睡眠
		//		void join():等待该线程终止。
		//		void setName(String name):改变线程名称 
		//		String getName(): 返回该线程的名称。
		//		static Thread currentThread():得到当前执行线程的对象
		//		线程的   等待状态及调度
		Thread t1=new Thread(new TaskA());
		Thread t2=new TaskB();

		t1.setName("TaskA");
		t2.setName("TaskB");

		//(精灵线程)守护线程,不是主要线程  //其他线程结束,他也跟着结束
		t1.setDaemon(true);
		
		t1.start();
		t2.start();
		
		//主线程 对t1掉join(),让 主线程进入 无限 等待状态      当t1结束 主线程恢复运行
		t1.join();

		//打印当前对象
		System.out.println(Thread.currentThread().getName());
		System.out.println("以结束");
	}
  • 线程异步处理
    在这里插入图片描述

4.6.3 实现Callable接口,并实现其call()方法

1 自定义类实现Callable接口
	public class task implements Callable<String> {
2 覆盖Call方法
	Override
		public String call()   throws Exception  {
		return "MyThread3.java";  
		}
3 创建线程池
  ExecutorService threadPool=Executors.newCachedThreadPool();
4>Callable任务提交给线程池
	 Future<String> future = threadPool.submit(new task()); 
	 //用Future对象   用于存储将来可能有的返回值
     //future可能没运行完,主函数需要返回值,主函数需要等待 直到拿到
     //等到需要的结果
	 future.get() 
	 es.shutdown();    //最后关线程池
		
注意:实现Callable接口,只能基于线程池使用。解决runnable的缺点
	(1)、不能抛异常
	(2)、没有返回值
	

4.6.4 继承Thread和实现Runnable的利弊

1、继承Thread
	 好处是——代码简单,可以直接使用Thread类中的方法,
	 弊端是:——如果已经有了父类,就不能用这种方法
2、实现Runnable接口
	好处是——即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
	弊端是:——,代码复杂,不能直接使用Thread中的方法,需要先获取到线程对象后,才能得到Thread的方法

4.7 线程的基本状态

在这里插入图片描述

1)初始状态
	线程对象创建
(2)、runnable状态
	就绪状态
		线程对象掉start()方法,没有获得cpu时间片
	运行状态
		线程获得cpu时间片
		线程失去CPU时间片——回到就绪状态
	等待状态
			有限等待状态
				对当前线程调Thread.sleep(毫秒数)方法 当前线程进入有限等待状态,
				时间结束,回到至就绪状态
			无线等待状态
				主线程对 t1调join()方法——主线程进入无限期等待状态,
				当t1线程对象执行结束,主线程回到就绪状态
	 阻塞状态:没有拿到对象的所标记
	 终止状态:run()结束

4.8 线程中的相关的方法

1)、static void sleep(long millis) 让当前线程睡眠
(2)、join()无线等待状态,等待该线程终止。
(3)、static Thread currentThread()得到当前执行线程的对象的引用
(4)、String getName():  返回该线程的名称。
(5)、void setName(String name): 改变线程名称 
(6)、Thread.year(); 主动放弃CPU

4.9 线程安全

4.9.1 临界资源线程安全问题

1)、资源存在于进程中——进程是拥有资源的,线程是不拥有资源的。
(2)、当多线程 共同访问一个对象(临界资源时),破坏的不可分割的操作(原子操作,
	  就可能发生数据的不一致。  
2、解决方案
	使用锁机制 synchronized
	 a:由于java中每个对象都有一个互斥锁标记,用来分配给线程(只能给一个线程),
	    所以使用 synchronized(s){   } 对s加锁的同步代码块,只有拿到s的所标记,
	    才能进入加锁的同步代码块
	 b:synchronized  作为方法的修饰符,对this加锁的同步代码块,只有拿到o的
	 	锁标记线	程,才能调用o的同步方法,o.add() 由线程执行,线程竞争一把锁  
	 	同步牺牲了并发效率
  • 处理一代码
class  MyList{
	String[] data ={"A","B","","",""};
	int index=2;
	public void add(String str){
		data[index]=str;
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		index++;
	}
	public void print(){
		for(int i=0;i<data.length;i++){
			System.out.println(data[i]);
		}
	}
}
	//主函数调用
	public static void main(String[] args) throws InterruptedException {
		MyList S=new MyList();
		Thread t1=new Thread(new Runnable(){
			public void run(){
			//解决方案 对同步代码块 S加锁,只有被锁标记的线程 ,
			//才能进入加锁的同步代码块
				synchronized (S) {//对临界资源加锁
					S.add("C");
				}				
			}
		});
		
		Thread t2=new Thread(new Runnable(){
			public void run(){
				synchronized (S) {
					S.add("D");
				}
			}
		});
		t1.start();
		t2.start();
		//join主线程进入 无限 等待状态,当t1结束主线程恢复运行
		t1.join();
		t2.join();
		
		S.print();	
	}
  • 处理二代码 对方法加锁
	public static void main(String[] args) throws InterruptedException {
		MyList5 S=new MyList5();
		Thread t1=new Thread(new Runnable(){
			public void run(){
				S.add("C");			
		}
	});
		Thread t2=new Thread(new Runnable(){
			public void run(){
				
				S.add("D");
		}
	});
			t1.start();
			t2.start();
			t1.join();
			t2.join();
			S.print();	
		} 
	}
class  MyList5{
	String[] data ={"A","B","","",""};
	int index=2;
	//	同步方法 牺牲了并发效率
	public synchronized void add(String str){
		data[index]=str;
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		index++;
	}
	public void print(){
		for(int i=0;i<data.length;i++){
			System.out.println(data[i]);
		}
	}
	

4.9.2 死锁问题(等待通信机制)

  • 产生原因
1)、同步代码块 进行了嵌套 
		synchronized(O1){  synchronized(O2){   }  }   线程1
		synchronized(O2){  synchronized(o1){   }  }  线程22)、线程1拿到了O1的锁标记,进入o1的同步代码块  这时需要o2的锁标记
(3)、线程2拿到了O2的锁标记,进入了o2的同步代码块,这是需要o1的锁标记
(4)、线程1和线程2 都不愿意舍弃自己手中的锁标记——从而造成了死锁问题,
	   导致程序不能正常运行
  • 解决方案 等待通知机制
1)、对o1调wait(),必须出现在对o1加锁的同步代码块中
		释放线程所拥有的所有锁标记,失去CPU, 进入等待状态,进入o1的等待队列
(2)、对o1调notify()/notifyAll() 必须出现在对o1加锁的同步代码块中
		将o1 的线程从等待状态释放出一个 或者全部线程,并没有释放o1的锁标记,
		将自己的程序执行完以后,释放锁标记	
public static void main(String[] args) throws InterruptedException {
		Object o=new Object();
		
		Thread t1=new  Thread(new Runnable(){
			public void run(){
			synchronized (o) {
				System.out.println("A");
				System.out.println("B");
				//将o1 的线程从等待状态释放出一个,将自己的程序执行完以后,释放锁标记
				o.notify();
				System.out.println("C");
				System.out.println("D");
				}
			}
		});
		
		t1.start();
		
		synchronized (o) {
			System.out.println("1");
			System.out.println("2");
			o.wait();//进入等待状态,进入o1的等待队列
			System.out.println("3");
			System.out.println("4");		
		}
	}

4.9.3 生产者和消费者问题(等待通信机制)

  • 背景
1)、栈空间(满足后进先出)   空间大小一定,两个线程
	(2)、一个线程t1作为生产者,生产 ——入栈  过程 将A_Z字母入栈
	(3)、一个线程t2作为消费者,消费 ——出栈  过程 弹栈 将Z_A弹出
    会出现下标越界异常 ——数组满了,往里放,——或者数组空了往外取
  • 解决方案
1)、生产者不是什么时候都生产,
	 数组长度满了就不生产,调this.wait()停止生产,进入等待队列
	 生产了之后 调this.notify() 将消费者从等待队列唤醒出来
(2)、消费者不是什么时候都消费的 
	数组里没东西了,就调this.wait()停止消费  ,进入等待队列
	消费了之后,就调this.notifyALl()   将生产者从等待队列唤醒出来 
  • 代码
class  MyStack{
	String[] data ={"","","","","",""};
	int index;
//	栈   存元素的方法 
	public synchronized  void push(String str){
		while(data.length==index){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.print(str+ "  "+"进栈"+"   ");
		data[index]=str;
		index++;
		print();
		this.notifyAll();//通知消费者 我生产了  进坑之前,释放另一个线程资源
	}
	//	出栈
	public synchronized  void pop(){
		while(index==0){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		index--;
		String o=data[index];
		data[index]="";
		System.out.print(o+ "   "+"出栈");
		print();
		
		this.notifyAll();//通知生产者 数组中消费了   进坑之前,释放另一个线程资源
	}
//	遍历函数
	public  void print(){
		for(int i=0;i<data.length;i++){
			System.out.print("   "+data[i]);
		}
		System.out.println();
	}
	//主函数
	public static void main(String[] args) throws InterruptedException {
		MyStack S=new MyStack();
		
		//将A到z存储到数组中
		Thread  task1=new Thread(new Runnable(){
			@Override
			public void run() {
				for(char i='A';i<'Z';i++){
					S.push(i+"  ");
				}
			}
		}) ;
		
		//将z到A出栈
		Thread  task2=new Thread(new Runnable(){
			@Override
			public void run() {
				for(char i=0;i<26;i++){
					S.pop();
				}
			}
			
		}) ;

			task1.start();
			task2.start();
			//task1.join();
			//task2.join();
			System.out.println("进程执行完了");
	}

4.9.4 数字的交替打印(等待通信机制)

1)、进入等待队列之前,——对o调notifyAll()
	(2)、执行了某些控制代码后——对o调 wait()
	(3)、进入wait() 前都要判断,否者程序无法结束,否者两个线程总有一个会
		   在等待队列里,没有线程将其换出
	原则:进入等待队列之前,将其他线程从等待队列里唤醒出来
//	交替打印 控制各线程 打印的次数  如字母打一个 ,数字打两个
//	等待通知的应用
	public static void main(String[] args) {
//		需要一个等待队列   因为t1和t2没有临界资源  
//		所以需要建一个Object的对象o,作为临界资源
		Object o=new Object();
		
		Runnable Task1=new Runnable(){
			@Override
			public void run() {
				synchronized (o) {
					for (int i = 1; i <= 52; i++) {
						System.out.println(i);
						
						if(i%2==0){
							 o.notifyAll();//进坑之前,释放字母线程
							try {
								if(i!=52) o.wait();
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					}
				}	
			}
		};
		
		Runnable Task2=new Runnable(){
			@Override
			public void run() {
				synchronized (o) {
					
					for (int i = 'A'; i <= 'Z'; i++) {
						System.out.println((char) i);
						o.notifyAll();//进坑之前,释放数字线程,
						try {
							if(i!='Z') o.wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}	
			}	
		};
		
		Thread t1=new Thread(Task1);
		Thread t2=new Thread(Task2);
		
		t1.start();
		t2.start();	
	}

4.9.5 Lock锁

4.9.5.1 基本使用
	//jdk 5.0 1)、Lock lock = new ReentrantLock();    // 接口 和 实现类2)、lock.lock();         // 对后面的代码块上锁 // 能执行更细的原子操作3)、lock.unlock();    // 解锁  // 最后一个try保证了锁对象一定释放锁资源	
class MyList12 {
	String[] data = { "A", "B", "", "", "" };
	int index = 2;
	Lock lock = new ReentrantLock();// 接口 和 实现类
	public void add(String str) {
		try {
			// 能执行更细的原子操作
			//lock.tryLock();尝试拿锁
			//lock.tryLock(time, unit)  尝试拿锁 设置时间,多少后不等了
			lock.lock();// 对后面的代码块上锁
			
			data[index] = str;
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			index++;
		} finally {
			lock.unlock();// 解锁
			// 最后一个try保证了锁对象一定释放锁资源
		}
	}
	public void print() {
		for (int i = 0; i < data.length; i++) {
			System.out.println(data[i]);
		}
	}

//主函数
public static void main(String[] args) throws InterruptedException {
		MyList12 S = new MyList12();// 临界资源
		
		Thread t1 = new Thread(new Runnable() {
			public void run() {
				S.add("C");
			}
		});

		Thread t2 = new Thread(new Runnable() {
			public void run() {
				S.add("D");
			}
		});
		t1.start();
		t2.start();

		t1.join();
		t2.join();
		
		S.print();
	}

4.9.5.2 lock锁通过condition对象–解决生产者消费者问题
	public static void main(String[] args) throws InterruptedException {
		// 生产者和消费者 this.wait() this.notifyALL()的用法
		// synchronized 所标记的用法
		// 模拟栈 存储元素
		MyStack1 S = new MyStack1();
	
		// 将A 到z存储到数组中
		Thread task1 = new Thread(new Runnable() {
			@Override
			public void run() {
				for (char i = 'A'; i < 'Z'; i++) {
					S.push(i + "  ");
				}
			}

		});

		// 将z 到A出栈
		Thread task2 = new Thread(new Runnable() {
			@Override
			public void run() {
				for (char i = 0; i < 26; i++) {
					S.pop();
				}
			}

		});
		task1.start();
		task2.start();
		// task1.join();
		// task2.join();
		System.out.println("进程执行完了");
	}
}

class MyStack1 {
	String[] data = { "", "", "", "", "", "" };
	int index;
	Lock lock = new ReentrantLock();// 接口 和 实现类  栈中加入锁对象
//	通过所对象调 newcondition()获得condition对象
//	获得两个等待队列
	Condition full=lock.newCondition();//数组满的时候等待
	Condition empty=lock.newCondition();//数组空的时候等待

	// 栈 存元素的方法
	public  void push(String str) {
		try {
			lock.lock();//加锁
			while (data.length == index) {
				try {
					full.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.print(str + "  " + "进栈" + "   ");
			data[index] = str;
			index++;
			print();
//			this.notifyAll();// 通知消费者 我上产了 进坑之前,释放另一个线程资源
			empty.signalAll();//通知消费者
		} finally {
			lock.unlock();
		}
	}

	// 出栈
	public  void pop() {
		try {
			lock.lock();
			while (index == 0) {
				try {
					empty.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			index--;
			String o = data[index];
			data[index] = "";
			System.out.print(o + "   " + "出栈");
			print();
//			this.notifyAll();// 通知生产者 数组中消费了 进坑之前,释放另一个线程资源
			full.signalAll();
			
			
		} finally {
			lock.unlock();
		}
	}

	// 遍历函数
	public void print() {
		for (int i = 0; i < data.length; i++) {
			System.out.print("   " + data[i]);
		}
		System.out.println();
	}

4.9.6 获取线程安全的集合

	public static void main(String[] args) {
		Set<String>  s1=new HashSet<String>();
		List<String>  l1=new ArrayList<String>();//线程不安全
		
		//线程不安全多线程一起往里面放东西 可能会出问题
		//工具类中的方法   同步意味线程安全   及s2可以拿去用 ,线程就安全了
		Set<String> s2= Collections.synchronizedSet(s1);
	
		List<String> l2= Collections.synchronizedList(l1);//经过工具处理线程就安全了
	   //l2 相当于vector 线程安全了
	}

4.9.7 读写锁的使用

  • 原理
    // 读写锁
    // JDk5.0 解决读写安全 与效率 的问题,读写分离
	// 互斥锁 只能分配一次 ,
	// 写锁分配出去了,那么读锁就不能被分配
	// 在写锁没分配出去时,允许多线程同时过来读
	// 有读锁运行时,不能分配写锁 
	//牺牲写的效率换来取度的效率
	ReadWriteLock dxs = new ReentrantReadWriteLock();
	Lock xs = dxs.readLock();// 读锁
	Lock ds = dxs.writeLock();// 写锁 读写锁配对使用	
class MyList1 extends ArrayList {
	// 读写锁
	ReadWriteLock dxs = new ReentrantReadWriteLock();
	Lock xs = dxs.readLock();// 读锁
	Lock ds = dxs.writeLock();// 写锁 读写锁配对使用
	// 互斥锁 只能分配一次 ,
	// 写锁分配出去了,那么读锁就不能被分配
	// 在写锁没分配出去时,允许多线程同时过来读
	// 有读锁运行时,不能分配写锁

	@Override
	public int size() {
		try {
			ds.lock();
			return super.size();
		} finally {
			ds.unlock();
		}
	}

	@Override
	public Object get(int index) {
		try {
			ds.lock();
			return super.get(index);
		} finally {
			ds.unlock();
		}
	}

	@Override
	public boolean add(Object e) {
		try {
			xs.lock();
			return super.add(e);
		} finally {
			xs.unlock();
		}
	}

	@Override
	public Object remove(int index) {
		try {
			xs.lock();
			return super.remove(index);
		} finally {
			xs.unlock();
		}
	}

	@Override
	public void clear() {
		try {
			xs.lock();
			super.clear();
		} finally {
			xs.unlock();
		}
	}

4.9.8 CopyOnWriteArrayList的使用 读效率高

   // 原理: 利用赋值数组的方式实现数组元素的修改
   //牺牲写的效率换取读 的效率(应用于都操作远大于写操作)
   //效率远高于读写锁
	public static void main(String[] args) {
		//CopyOnWriteArraySet<String> list1= new CopyOnWriteArraySet<String>();
		CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<String>();
		list.add("sss");
	}
	//源码:
	 public boolean add(E e) {
	        final ReentrantLock lock = this.lock;
	        lock.lock();
	        try {
	            Object[] elements = getArray();
	            int len = elements.length;
	            Object[] newElements = Arrays.copyOf(elements, len + 1);
	            newElements[len] = e;
	            setArray(newElements);
	            return true;
	        } finally {
	            lock.unlock();
	        }
	    }

4.9.9 线程安全的map实现类 ConcurrentHashMap

  • 分段锁 将map分成16断,对每一段进行加锁。效率高。用于Map获取安全的并且效率高的场景。

4.9.10 线程安全的Queue实现类 ArrayBlockingQueue

  • 原理:使用CAS 无锁算法,实现线程安全,比较交换算法实现。乐观锁(雨伞比喻)
  • a[2] 赋值,如果为空,赋值,不为空,不赋值。先对预先的值进行预判。
  • 无锁算法解决生产者消费者问题。
阻塞队列:
	put() 添加元素到队列中,如果对列满,则等待
	take() 删除队列头部元素,如果队列为空则,等待
	public static void main(String[] args) {
//		Queue 子接口 BlockingQueue   
//		实现类 ArrayBlockingQueue()数组实现  有界  存在数据满的情况   
//		     linkedBlockingQueue*()链表实现  无界  不存在数据满的情况
		
		BlockingQueue<String> S=new ArrayBlockingQueue<String>(2);
		Runnable r1=new Runnable(){
			@Override
			public void run() {
				for(int i=1;i<=100;i++){
					try {
						S.put("A"+i);
						for (String o : S) {
							System.out.print(o+"  ");
						}
						System.out.println();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}	
		};
		Runnable r2=new Runnable(){
			@Override
			public void run() {
				for(int i=0;i<=100;i++){
					try {
						S.take();
						for (String o : S) {
							System.out.print(o+" ");
						}
						System.out.println();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}	
		};
//		创建启动线程池
		ExecutorService es=Executors.newFixedThreadPool(2);
		es.submit(r1);//将任务提交给线程池
		es.submit(r2);
		es.shutdown();//执行后完关闭线程池
	}

4.9.11 原子锁

  • 比较交换算法
public class Test19AtomicInteger20原子操作 {
	//将i从1+到100000   程序算达不到100000
	//多线程访问一个简单表量一样会出问题如++1,等
	
	//解决方案  JDK5.0 类Concurrent 包下面的子包   atomic(原子)
	//class  AtomicInteger》>>>>>
	//	AtomicBoolean这两个原子类用的最多
	
	//	实现一个整数的原子更新(不可分割++1,--1等)
	
	//	(包装integer 整数不被破坏,没上锁,上锁没效率)
	
	//	无锁算法    cas算法  比较交换 更新i的值   10变11,期望值是10,
	//  不一样,撤销操作
	static int i=0;
	//	建一个对象原子类对象a,给一个初始值0
	static AtomicInteger a=new AtomicInteger(0);	
	static Integer b=Integer.valueOf(0);//Integer对象不可变
	static MyObject obj=new  MyObject();
	
	public static void main(String[] args) throws InterruptedException {
		Thread []ts=new Thread[10];
		for(int k=0;k<ts.length;k++){
			ts[k]=new Thread(new Runnable(){
				@Override
				public void run() {
				 for(int k=1;k<=10000;k++){
					 i++;
					 a.incrementAndGet();//先加一在取值
//					 b.getAndDecrement();//减一
//					 b.getAndIncrement();//先取值在加一
					 synchronized (b) {
					 //分别对 0 加锁,对2 加锁。导致没有临界资源
						b=Integer.valueOf(b.intValue()+1);
					}
					 synchronized (obj) {
							obj.x++;
						}
				 }	
				}
				
			});
			ts[k].start();
		}
		for(int k=0;k<ts.length;k++){
			ts[k].join();
		}
		System.out.println(i);
		System.out.println(a);
		System.out.println(b);	
		System.out.println("obj的值   "+obj.x);
	}
}
//建一个类
class  MyObject{
	public int x;
}
* 结果
~~~java
	99944
	100000
	98927
	obj的值   100000

4.9.12 线程安全小结

线程不安全的原因及解决思路解决:
	1、给共享的资源加把锁,
	2.让线程也拥有资源,不用去共享进程中的资源。
	3.从算法上规避线程不安全的因素
解决方案:
		——1.、lock方式:为资源加锁。
		例如synchronized修饰同步代码块或者方法进行加锁
		——2.多实例、或者是多副本(ThreadLocal):对应着思路2ThreadLocal可以为每个线程的维护一个私有的本地变量;
		——3. 使用 java.util.concurrent 下面的类库:有JDK提供的线程安全的集合类
		如:CopyOnWriteArrayListCopyOnWriteArraySetConcurrentHashMapConcurrentLinkedQueue
		——4、针对集合,还可以使用java.util.Collections工具类的方法将线程不安全的集合包装成线程安全的集合
			如:Collections.synchronizedSet();         Collections.synchronizedList();  Collections.synchronizedMap();
	threadlocal
		原理:
			变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象。

4.10 线程池

4.10.1 、定长\可变长线程池

//	java.util.concurrent并发包	JDK5.0 	任务执行器  
//接口  Executor  子接口 ExecutorService(线程池)
//借助工厂创建固定长度线程池对象,可变长度线程池对象,1)、ExecutorService es=Executors.newFixedThreadPool(2); //给一个初始长度2
	   ExecutorService es=Executors.newCachedThreadPool();	
		//(线程已经启动,但没有任务)
		//任务的需要 扩充线程() 
		//可变长度线程池,避免重复的创建线程销毁线程.2)、新建任务对象task1 ,实现runnable接口,实现run方法
(3)、es.submit(task1);//把任务提交给线程池4)、es.shutdown();	 //		执行完之后需要    关    线程池
	public static void main(String[] args) {
	//借助一个工厂拿到线程池对象,给一个初始长度2
		ExecutorService es=Executors.newFixedThreadPool(2);
//		可变长度线程池              固定长度线程池
//		 前者线程固定       后者    随任务的需要 扩充线程(避免重复的创建线程销毁线程)
//		              让一个线程反复的去执行多个任务
//		ExecutorService es=Executors.newCachedThreadPool();

		Runnable  r1=new Runnable(){
				@Override
				public void run() {
					for (int i=0;i<=10;i++){
						System.out.println("&&&"+"  "+i);
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}//控制打印节奏
					}			
				}	
			};
			
		Runnable  r2=new Runnable(){
			@Override
			public void run() {
				for(int i=0;i<=10;i++){
					System.out.println("$$$"+"  "+i);
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}	
			}	
		};
	Runnable  r3=new Runnable(){
			@Override
			public void run() {
				for(int i=0;i<=10;i++){
					System.out.println("###"+"  "+i);
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
			}
		};
//		创建了三个    (线程)任务 	
//		调用     线程池的一个方法 submit()    把任务提交给线程池
//		线程池中有两个线程     两个线程已经启动,但没有任务)
//		两个线程执行两个 线程任务人r1,r2,当执行完其中一个时,在执r3
		es.submit(r1); 
		es.submit(r2);//把任务提交给线程池
		es.submit(r3);
		
//		执行完之后需要    关    线程池
		es.shutdown();	
}

4.10.2 、Callable与线程池的结合

//		创建线程池
		ExecutorService es=Executors.newCachedThreadPool();
		
		Callable<Integer> task1=new Callable<Integer>(){
			@Override
			public Integer call() throws Exception {
				System.out.println("task1 start working");
				int result=0;
				for(int i=1;i<100;i+=2){
//					求所有奇数的和
					result+=i;
					Thread.sleep(100);
				}
				System.out.println("task1 end working");
				return result;
				
			}	
		};
		Callable<Integer> task2=new Callable<Integer>(){
			@Override
			public Integer call() throws Exception {
				System.out.println("task2 start working");
				int result=0;
				for(int i=2;i<=100;i+=2){
//					求所有偶数的和
					result+=i;
					Thread.sleep(100);
				}
				System.out.println("task2 end working");
				return result;
				
			}
		};
	 Future<Integer>  f1=es.submit(task1);
	 Future<Integer>  f2=es.submit(task2);
	 //	f1,f2可能没运行完,主函数需要返回值,主函数需要等待,直到拿到等到需要的结果
	  int result= f1.get()+f2.get();
	  System.out.println(result);
			 
	//	最后关线程池
		es.shutdown();

4.10.3 、ForkJoinPool 线程池

  • 1.7版本、突破性,线程支持多核CPU
  • 线程池原理,多个线程提前放到池里,线程池帮我们调多个CPU同运行多个线程。
  • 原理:大任务划分成小任务并行计算 最后汇总
	public static void main(String[] args) {
//		打印当前cpu的核数
		System.out.println(Runtime.getRuntime().availableProcessors());
		
//		ForkJoin  JDK   7.0  为多核  cpu 而生
//		原理:大任务划分成小任务并行计算   最后汇总
//		 利用  无锁  临界资源 提高并行效率  工作窃取算法
		//ForkJionPool类  创建下  线程池
		ForkJoinPool pool=new ForkJoinPool();
		
//		 ForkJoinTask类任务对象   
//		 两个子类  RecursiveTask<v>  有返回值
//		 	   RecursiveAction   没有返回值
		
		 Addtask main=new Addtask(1,100000);
		 long result=pool.invoke(main);//提交一个大任务给线程池
		 System.out.println(result);
		 
//		                            多核多线程
//		 Addtask t1=new Addtask(1,50000);
//		 Addtask t2=new Addtask(50001,100000);	
	}
----------------------------------------------------------
//    写一个类 既要表示大任务 也要表示小任务
class Addtask extends RecursiveTask<Long>{
	int start;
	int end;//负责从几开始到从几结束的累加和
	static final int THRESHOLD=5000;
	
	//任务小于5000时就不在分割,自己算,阀值
	public Addtask(int start, int end) {
		super();
		this.start = start;
		this.end = end;
	}
	
	@Override
	protected Long compute() {
//		如果start和and 之间低于阀值,那么自己计算
		if((end-start)<=THRESHOLD){
			long result=0;
			for(int i=start;i<=end;i++){
			 result+=i;
			}
			return result;
		}else{
			 int middle =(start+end)/2;
			 Addtask  task1=new Addtask(start,middle);
			 Addtask  task2=new Addtask(middle+1,end); 
			 //方法,表示让t1,t2,执行,递归调用compute方法
			 this.invokeAll(task1,task2);
			 long r1=task1.join();//执行完,拿到返回值
			 long r2=task2.join();
			 return r1+r2; 
		}//否者将将任务分为两个子任务
	}
}

  • 无返回值实现
//数据映射  先把数据分散了,然后转化成你需要的结果,然后在汇总
public class Test19Forkjoin22另一个类 {
	public static void main(String[] args) {
		// ForkJoinTask类任务对象
		// RecursiveAction <void> 没有返回值
		// 实现数组排序的算法 让多个cpu 同步运行,提高工作效率
		Random random = new Random();// 随机数发生器

		// 元素赋值,生成数组。任务完成排序。
		int[] data = new int[5000000];
		for (int i = 0; i < data.length; i++) {
			data[i] = random.nextInt(5000);
		}

		ForkJoinPool pool = new ForkJoinPool();
		MySortTask main = new MySortTask(data, 0, data.length);
		pool.invoke(main);

		 jdk1.8 的方法对数组进行从大到小排序 2中方法
		// Arrays.parallelSort(data);//效率高
		// Arrays.sort(data);

		for (int i = 0; i < 5000; i++) {
			System.out.println(data[i]);
		}
	}
}

class MySortTask extends RecursiveAction {
	// 实现无返回值的一个方法
	int[] data;
	int start;// 保函起始下标
	int end;// 结束下表不保函在内
	static final int THRESHOLD = 100;// 阀值 ,1000以内不拆分

	public MySortTask(int[] data, int start, int end) {
		super();
		this.data = data;
		this.start = start;
		this.end = end;
	}

	@Override
	protected void compute() {
		if (end - start <= THRESHOLD) {
			Arrays.sort(data, start, end);
		} else {
			int middle = (start + end) / 2;
			MySortTask task1 = new MySortTask(data, start, middle);
			MySortTask task2 = new MySortTask(data, middle, end);

			invokeAll(task1, task2);// 让任务同时执行,各自排序
			// 将两个数有序的小数组合并成两个大数组
			merge(middle);
		}
	}
	// 将两个数有序的小数组合并成两个大数组
	public void merge(int middle) {
		// 将数组中的一部分 原有的一段 ,形成新数组
		// int[] b=Arrays.copyOfRang(a,2.6);
		int[] a = Arrays.copyOfRange(data, start, middle);// 复制
		int[] b = Arrays.copyOfRange(data, middle, end);// 复制
		int x = 0;
		int y = 0;
		for (int i = start; i < end; i++) {
			if (x == a.length) {
				data[i] = b[y];
				y++;
			} else if (y == b.length) {
				data[i] = a[x];
				x++;
			} else {
				if (a[x] < b[y]) {
					data[i] = a[x];
					x++;
				} else {
					data[i] = b[y];
					y++;
				}
			}
		}

	}

五、异常

1、概念:
	1. 异常:程序中出现的一些非正常的情况。
	2. 异常处理:当不正常情况出现时,程序将执行准备好的一段代码,以免给用 带来损失。
2、分类
	异常的父类 Throwable,位于java.lang 中
		a. String getMessage():获取字符串类型的异常信息。
		b. void printStackTrace():打印方法调用栈的异常详细信息。
	子类
	Error 类
		紧靠程序无法恢复的严重的错误 ——例如:方法调用过程中栈溢出或是内存不足
		特点:程序中遇到Error,一般情况下,程序中时无法处理。
	Exception1RuntimeException(运行时异常、未检查异常)Java编译器不会检查它,运行时报错。这类异常可以避免,可以处理也可以不处理
		2、非RuntimeException(非运行时异常、已检查异常):
			特点:Java编译器会检查它,出现这类异常,编译不通过,所以必须处理。

六、Optional类

在这里插入图片描述

七、Stream的使用

7.1、List->Stream->List

在这里插入图片描述

7.2、Stream中间操作

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

7.3、Stream终端操作

在这里插入图片描述

7.4、流收集器Collect(Collectors.)

在这里插入图片描述

7.5、流分组Group

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 先收集在转换
    在这里插入图片描述
  • 先转换在链接
    在这里插入图片描述

八、泛型

8.1、基本概念和泛型方法

在这里插入图片描述

在这里插入图片描述

8.2、泛型类泛型接口

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值