Java基础知识复习

一,Java基础知识复习

1,break在二重循环中使用,默认情况跳出最近的循环,如果跳出外层循环需要使用标记。
2,数组的声明:(数组包含元素是引用数据类型,必须,为每个元素都实例化,否则空指针)
    (1)int[] nums; nums = new int[5];
    (2)int[] nums = new int[10];
    (3)int[] ages = new int[]{21,18,22,25,30};
    (4)int[] ages = {21,18,22,25,30};
3,byte,short,char三者在计算时会转换成int类型。
浮点运算陷阱:float和double都不能进行精确运算,而要使用BigDecimal类解决,时间转换SimpleDateFormat
基本数据类型

·8种基本数据类型

·3种引用数据类型:类,接口,数组

·8中基本数据类型对应的包装类,能被final修饰,但不能被继承

·String和StringBuffer类也是被final修饰的
基本数据类型:

第一类:整型–>byte short int long

第二类:浮点–>float double

第三类:逻辑–>boolean

第四类:字符–>char

(基础数据类型在栈,里面直接分配内存,而引用数据则是通过堆里的对象来对栈中的内容进行引用)

基本数据类型由低级到高级分别为:(byte、short、char)-> int -> long -> float -> double

题目1:switch语句能否作用在byte上,能否作用在long上,能否作用在String上?

答:在switch(expr1),expr1只能是一个整数表达式或者枚举常量(更大字体),整数表达式可以是int基本数据类型或者int的包装类Integer,由于byte,short,char都可以隐式转换为int,所以这些类型以及他们的包装类也是可以用switch的。显然,long和String类型都不能被隐式转换为int类型(long可以强制转换为int,但是会丢失精度),所以他们不能作用于swtich语句中

题目2:Short s1 = 1;s1 = s1 + 1;有什么错?Short s1 = 1;s1 += 1;有什么错?为什么?
对于 short s1 = 1; s1 = s1 + 1;由于 s1+1运算时会自动提升表达式的类型,所以结果是 int
型,再赋值给 short 类型 s1时, 编译器将报告需要强制转换类型的错误。
对于 short s1 = 1; s1 += 1;由于 +=是 java 语言规定的运算符, java 编译器会对它进行特殊
处理,因此可以正确编译

4,一个方法只能有一个可变参数,可变参数必须在方法参数列表的最后。
5,排序

(1)冒泡排序:两个相邻的元素进行比较,小的向前,大的向后。

           for(int i = 0;i<arr.length-1;i++){
                   for(int j=0;j<arr.length-i-1;j++){
                           if(arr[j]>arr[j+1]){
                                int t = arr[j];
                                arr[i]=arr[j+1];
                                arr[j+1]=t;
                       }
                     
                   }
           }

在这里插入图片描述
(2)插入排序:从第二个元素开始,向前找到插入的位置,保证前面的元素都有顺序。

     for(int i=1;i<arr.length;j++){
          for(int j=i-1;j>=0;j--){
                 if(arr[j]>arr[j+1]){
                         int t = arr[j];
                         arr[j] = arr[j+1];
                         arr[j+1] = t;
                  }
          }
     }

在这里插入图片描述
(3)选择排序:选择一个元素,让选择依次和后面的元素进行比较,小的向前,大的向后。

     for(int i = 0; i<arr.length-1;i++){
          for(int j=i+1; j<arr.length;j++){
            if(arr[i]>arr[j]){
               int t = arr[j];
               arr[i] = arr[j];
               arr[i] = t;
            }
          }
     }

在这里插入图片描述
(4)快速排序:采用递归分治的思想

public static void quickSort(int[] arr,int start,int end){
        int i=start;
        int j=end;
        int pivot=arr[start];
        while(i<j){
            //从后向前
            while(i<j&&arr[j]>pivot){
                j--;
            }
            arr[i]=arr[j];
            i++;
            //从前向后
            while(i<j&&arr[i]<pivot){
                i++;
            }
            arr[j]=arr[i];
            j--;
        }

        arr[i]=pivot;

        //前面有元素
        if(i-1>start){
            quickSort(arr, start, i-1);
        }
        //后面又元素
        if(j+1<end){
            quickSort(arr, j+1, end);
        }


    }

在这里插入图片描述
(5)二分查找:二分查找是一种查询效率非常高的查找算法,又称折半查找。算法思想:有序的序列,每次都是以序列的中间位置的数来与待查找的关键字进行比较,每次缩小一半的查找范围,直到匹配成功。

public static int binarySearch(int[] arr,int key){
     int low = 0;
     int upper = arr.length-1;
     while (low<=upper){
         int mid = (low+upper)/2;
         if(key<arr[mid]){
             upper = mid-1;
                
         }else if{
             low = mid +1;
         }else{
             return mid;
         }
     }
     return -1;
}

在这里插入图片描述

6,重载与重写

(1)方法重载:在同一个类中,方法名相同,参数列表不同(类型,个数,顺序不同)
注意:方法的重载和返回值无关。
(2)方法重写:方法名,参数列表和父类完全一致,返回值类型,访问修饰符,方法中抛出的异常都要低于父类。

7,this关键字和super关键字

This关键字:
(1)this关键字第一种用法:this.属性,this.实例方法,解决局部变量和成员变量同名时,使用this.属性调用成员变量。
(2)this关键字第二种用法:this();注意:必须是构造方法的第一条语句,只能调用一次。

Super关键字:
(1)super的第一种用法:调用父类的属性和方法,super,属性名和super.方法名
(2)super的第二种用法:调用父类的构造方法。

This和Super的区别:
1,this表示当前对象,super表示父类对象。
2,this调用本类的属性,方法,也可以继承父类的属性和方法,super只能调用父类的属性和方法。
3,this()调用本类的其他构造方法,super()调用父类的构造方法,this()和super()要求必须是构造方法首行,不能同时使用。

8,向上转型和向下转型

(1)向上转型:只能调用父类的属性和方法。或子类重写的父类方法,不能调用子类特有的属性和方法。
(2)向下转型:
注意1:向下转型的前提,必须先向上转型,才能向下转回真实类型,否则出现ClassCastException。
注意2:可以调用子类特有的属性和方法,可以调用父类继承的属性和方法,或子类重写的方法。

9,抽象类

作用:
1,可被子类继承,提供共性属性和方法。
2,可声明利用,更自然的使用多态。
特点:
1,不能被实例化,也包含构造方法。
2,抽象类中可以包含抽象方法,也可以非抽象方法。
3,包含抽象方法的类一定是抽象类。
4,子类必须实现抽象类中抽象方法,除非子类也是抽象类。
5,抽象类不能创建自己的类对象。原因:abstract修饰的类内有可能存在abstract修饰的方法,而abstract修饰的方法是没有方法体的,如果说创建了abstract修饰类对应的对象,不能执行没有方法体的abstract方法。

抽象方法:
1,不能被实例化,包含构造方法。
2,抽象类中可以包含抽象方法,也可以有非抽象方法。
3,包含抽象方法的类一定是抽象类。
4,子类必须实现抽象类中抽象方法,除非子类也是抽象方法。

abstract不能与static放在一起:因为abstract没有方法体,static要用类对象调用。
abstract不能与final放在一起:因为abstract必须被重写,final修饰的方法不能被重写。

10,static关键字

1,用法:修饰属性,方法,静态代码块,静态内部类,静态导入。
2,特点:
(1)静态方法中可以直接访问静态属性,不能直接访问非静态属性和方法,不能使用this,super关键字(不能用于外部类)。
(2)非静态方法可以直接访问静态属性,静态方法。
(3)在代码中没有创建对象时,可以通过类名直接使用静态成员变量,和对象无关。

11,final关键字

final修饰的成员变量定义时必须初始化,并且赋值之后无法修改,修饰的成员方法不能被子类重写,修饰的类不能被继承。

12,代码块

1,静态代码块:只要类加载,当前静态代码块中的内容就一定会执行,并且有且只执行一次,整个类的初始化过程。
2,构造代码块:初始化当前类的所有类对象,只要调用构造方法,一定会执行对应的构造代码块。每次在执行构造方法之前,如果当前类中有构造代码块,优先执行构造代码块,然后在执行构造方法!
3,局部代码块:在类的局部位置 作用:限定当前变量的声明周期。

13,定义接口(关键字interface)

1,包含公开的静态常量,公开的抽象方法。
2,没有构造方法,不能创建对象。
3,JDK1.8之后包含静态方法,不能被继承和默认方法,default不是访问修饰符,可以被继承。
4,静态属性程序一加载时就初始化放在栈中,实例属性需要实例化后才加载存放在堆中。

接口和抽象类的异同点:
相同:都不能创建对象,都可以定义抽象方法,并且一定要在子类重写
不同:关键字不同,抽象方法中可以有普通方法,接口只能有抽象方法,抽象方法可以使用任意权限修饰符,接口只能用public,抽象类只能单继承,接口可以实现多个接口。

14,内部类

一,成员内部类(当外部类,内部类存在重名属性时,会优先访问内部类属性)
特点:
1 成员内部类可以使用任意访问修饰符(外部类只能是public或默认)
2 成员内部类可以直接访问外部类的属性和方法
3 成员内部类中属性和外部类的属性同名时,使用外部类名.this访问外部类的属性
4 成员内部类不能包含静态成员,但是可以包含静态常量

内部类调用:
Outer out = new Outer();
Outer.Inner in = out.new Inner();

二,静态内部类(级别和外部类一样,为外部类提供功能)
特点:
1 静态内部类可以使用任意访问修饰符
2 静态内部类不能直接访问外部类的实例属性和方法,可以直接访问静态的属性和方法
3 静态内部类可以包含静态成员
4 不依赖外部类对象,可直接创建或通过类名访问,可声明静态成员,只能直接访问外部类的静态成员(实例成员需要实例化外部类对象)

内部类调用:
Outer.Inner inner = new Outer.Inner();

三,局部内部类(级别和局部变量相同,使用范围只能当前方法中使用)
特点:
1 不能使用任何访问修饰符
2 如果局部内部类所有方法是非静态方法,可以直接访问外部类的实例属性和方法。如果局部内部类所在方法是静态方法,只能访问外部类的静态属性和方法
3 局部内部类可以访问局部变量,但是局部变量必须是final(JDK1.8 final可以省略,对象的声明周期长于局部变量)
4 局部内部类也不能声明静态成员,可以使用静态常量

四,匿名内部类(使用范围在当前方法中使用,如果要在方法外使用,要加static修饰)
特点:
1 创建匿名内部类可以使用接口,抽象类,普通类,必须实现接口或抽象类中抽象方法
2 匿名内部类不能手动添加构造方法,不能包含静态的成员
3 匿名内部类中一般不包含特有的方法,不能直接访问,可以通过访问方法调用或内部对象调用
4 匿名内部类生成的class文件名:类名$编号.class

15,创建对象的四种方式

1 new关键字创建
2 反射:getClass():获取类的类对象
hasCode():返回对象的hash值
newInstance();构建对象
invoke();调用方法

3 反序列化(不调用构造方法):
序列化中实现的两个接口:serializable不调用构造方法,Externalizable会调用无参构造方法
4 克隆:要求实现cloneable接口,不调用构造方法

16,常见的字符串方法

public char charAt(int index):根据下标获取字符
public boolean contains(String str):判断当前字符串中是否包含str
public char[] toCharArray():将字符串转换成数组
public int indexOf():查找str首次出现的下标,存在则返回该下标;不存在,则返回-1
public int length():返回字符串的长度
public String trim():去掉字符串前后的空格
public String toUpperCase():将小写转成大写
public boolean endsWith(String str):判断字符串是否以str结尾
public String replace(char oldChar,char newChar):将旧字符串替换成新字符串
public String[] split(String str):根据str做拆分
public String subString(int beginIndex,int endIndex):在字符串中截取出一个字符串

16.1,string,stringBuffer,stringBuilder区别

string字符串常量不可变使用字符串拼接时会开辟新空间
stringBuffer字符串变量可变线程安全字符串拼接直接在字符串后追加
stringBuilder字符串变量可变非线程安全字符串拼接直接在字符串后追加

16.2,类型转换

基本类型转换成字符串:
1 在后边加引号空格
2 String.valueOf()
3 Integer.toString()
字符串转换成基本类型:
Integer.parseInt()

16.3,字符串intern方法(返回字符串对象的规范化表示形式)

使用intern方法如果常量池中没有,则把对象复制一份(或对象引用)放入常量池中,返回常量池中的对象,如果常量池中存在,则直接返回。JDK1.7之前是复制一份放入常量池,JDK1.7之后(包括jdk1.7)则把对象引用赋值到常量池

17,变量相关知识

·成员变量与局部变量:成员变量在对象堆内存,局部变量在栈内存中

·实例变量与类变量:实例变量随对象创建存在堆内存中,类变量随方法创建而存在方法区中

成员变量和静态变量的区别:

   1、成员变量所属于对象。所以也称为实例变量。

      静态变量所属于类。所以也称为类变量。

   2、成员变量存在于堆内存中。
        静态变量存在于方法区中。

   3、成员变量随着对象创建而存在。随着对象被回收而消失。

      静态变量随着类的加载而存在。随着类的消失而消失。

   4、成员变量只能被对象所调用 。

      静态变量可以被对象调用,也可以被类名调用。

   所以,成员变量可以称为对象的特有数据,静态变量称为对象的共享数据。

成员变量和局部变量的区别

     成员变量:

          1、成员变量定义在类中,在整个类中都可以被访问。

          2、成员变量随着对象的建立而建立,随着对象的消失而消失,存在于对象所在的堆内存中。

          3、成员变量有默认初始化值。

      局部变量:

          1、局部变量只定义在局部范围内,如:函数内,语句内等,只在所属的区域有效。

          2、局部变量存在于栈内存中,作用的范围结束,变量空间会自动释放。

          3、局部变量没有默认初始化值 

      在使用变量时需要遵循的原则为:就近原则

      首先在局部范围找,有就使用;接着在成员位置找。
18,java中==和eqauls()的区别?

==是运算符,用于比较两个变量是否相等,对于基本类型而言比较的是变量的值,对于对象类型而言比较的是对象的地址.

equals()是Object类的方法,用于比较两个对象内容是否相等.

19,& 和 &&的区别

基础的概念不能弄混:&是位操作,&&是逻辑运算符.需要记住逻辑运算符具有短路特性,而&不具备短路特性.(短路特征指的是如果前面的代码能够得出结果,后面的代码不会执行)

二,集合

List集合
ArrayList集合的使用

特点:有序,有下标,可以重复,存储结构:数组
源码分析:
1 调用无参构造方法创建ArrayList集合,长度是size=0,elementData存储元素的数组
2 当添加第一个元素时,容量扩充为10,每次扩容大小都是原来的1.5倍
3 remove方法使用System.arrayCopy实现移动,效率高
4 迭代器中有一个cursor,获取一个元素后cursor+1,如果cursor==size,不能获取

LinkedList集合的使用

特点:有序,有下标,可以重复,存储结构:双向链表
源码分析:
1 构造方法没有任何操作,属性size=0元素个数,head头节点,Last最后一个节点
2 静态内部类Node:属性,item:数据,next:下一个元素,
prev:上一个元素
3 add方法中每添加都会形成一个节点,first属性指向第一个元素,last指向最后一个元素,每个节点都有前驱和后继

ArrayList和LinkedLind区别

1 ArrayList的存储结构是数组,LinkedList存储结构是双向链表
2 ArrayList的查找遍历比较快,插入和删除相对较慢,因为使用本地方法优化,并不一定比LinkedList慢
3 LinkedList的插入和删除比较快,查找遍历比较慢

类型转换

集合转成数组:

ArrayList<String> cities = new ArrayList<>();
cities.add("上海");
String[] arr = cities.toArray(new String[0]);

数组转成集合:

String[] name={"zhangsan","lishi"};
List<String> list = Arrays.asList(names);
Set集合
HashSet集合的使用

特点:存储结构:哈希表(本质数组+链表+红黑树(JDK1.8)),存储重复依据:hashCode和equals方法
存储过程:
(1)根据hashCode计算存储的位置,如果此位置没有元素则添加
(2)如果此位置有元素,再比较equals,如果equals相同,拒绝添加,否则形成链表添加

hashSet.contains():判断添加的数值是否存在,数据添加用add

迭代器:

Iterator<String> it = hashSet.iterator();
	while(it.hasNext()){
	System.out.println(it.next());
}
LinkedHashSet集合的使用

特点:存储结构:哈希表,有序,没有下标,不能重复
Linked:链表保存添加元素的顺序

TreeSet集合的使用

特点:存储结构:哈希表,有序,没有下标,不能重复,对元素进行排序,实现了SortSet接口
重复依据:Comparable接口中compareTo(),如果compareTo方法返回的是0,重复元素,拒绝添加。

迭代器:

Iterator<Student> it = treeSet.iterator();
Map集合的使用

特点:
1 存储的都是键值对key-value
2 键不能重复,值可以重复
3 无序,没有下标,添加用put

迭代器:
一,keySet()遍历,返回是键的Set集合

Set<String> keysets = map.keySet();
for(String key:keySet()){
   System.out.println(key+"=="+map.get(key));
}

二,entryset返回是键值对映射的集合

Set<Map.Entry<String,String>> entries = map.entryset;
LinkedHashMap集合的使用

有顺序的HashMap

Properties属性结合(Hashtable的子类)

特点:
1 存储时属性名和属性值
2 key和value都是字符串
3 没有泛型
4 和流有关

相关方法:
properties.setProperty():添加元素,不能用put,类型不对
System.getProperty():获取系统属性
properties.list():遍历
properties.load():加载文件
properties.store():保存到文件

迭代器:
1 keySet
2 entrySet
3 stringPropertyNames

Set<String> pronames = properties.stringPropertyNames();
.iter遍历,getProperty获取属性值
TreeMap集合的使用

特点:实现了SortedMap接口(map的子接口),可以对key自动排序,key需实现Comparable接口中compareTo()方法,不能重复,遍历:keySet(),entryset

HashMap集合的使用

存储结构:哈希表(JDK1.7数组+链表JDK1.8数组+链表+红黑树)
源码分析:
1 HashMap刚创建时,table是null,为了节省空间,当添加第一个元素时,table容量调整为16,resize()扩容
2 当元素个数大于阈值(16*0.75=12)时,会进行扩容,扩容后大小为原来的2倍。目的是减少调整元素的个数
3 jdk1.8当每个链表长度大于8,并且数组元素个数大于等于64时,会调整为红黑树,目的提高执行效率。(treeifyBin()方法变成红黑树)
4 jdk1.8当链表长度小于6时,调整成链表
5 jdk1.8以前,链表是头插入,jdk1.8以后是尾插入

HashMap里位置如何计算:
hashCode自身的高16位或低16位异或比较,目的:减少冲突,避免链表太长,得到的结果与15(1111)的二进制进行与运算,得到0-15下标。table = new Node[16] HashMap下标位置

Comparator比较器

1 可以实现定制比较,元素自身提供的比较规则称为自然排序
2 compare(o1,o2),如果返回值为0,则为重复元素
3 使用Comparator比较器,元素类型可不实现Comparable接口,并且优先级高于Comparable接口
4 实现字符串按照长度排序,如果长度相同,按照编码顺序

三,线程

进程和线程的区别

1 进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位
2 一个程序进行后至少有一个进程
3 一个进程可以包含多个线程,但是至少需要一个进程
4 进程间不能共享数据段地址,但同进程的线程之间可以

线程
组成:

1 任何一个线程都具有基本的组成部分:
CPU时间片:操作系统(os)会为每个线程分配执行时
2 线程的逻辑代码

运行数据:

1 堆空间:存储线程需使用的对象,多个线程可以共享堆中的对象
2 栈空间:存储线程需使用的局部变量,每个线程都有独立的栈

线程的特点:

1 线程抢占式执行:效果高,可防止单一线程长时间独占CPU,只有主线程运行main方法
2 在单核CPU中,宏观上同时执行,微观上顺序执行

创建线程:

第一种方式:继承Thread类,重写run方法
第二种方法:实现Runnable接口,重写run方法(Runnable是可运行对象,需要创建线程)

Thread thread  =  new Thread(myRunnable,"子线程");
一个myRunnable可以对应多个线程
获取线程id和线程名称

一,获取id
第一种方法:使用线程类的getId()和getName()获取
第二种方法:使用Thread.currentThread().getID,Thread.currentThread().getName
二,修改线程名称
第一种方法:setName()方法
第二种方法:使用构造方法

线程常用方法

休眠:休眠之后,不会争抢CPU,休眠时间到了,进入就绪状态参与争抢CPU
public static void sleep(long millis)
当前线程主动休眠millis毫秒

放弃:把CPU的使用权让给别的线程(优先级和让出线程相同或高于让出线程)
public static void yieId()
当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片

加入:加入主线程,阻塞主线程,直到加入线程执行完毕
public final void join()
允许其他线程加入到当前线程

优先级:线程对象.setPriority(int) 线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多

线程打断:线程对象.interrupt() 打断线程,被打断线程抛出InterruptedException异常

线程有两类:用户线程(前台线程),守护线程(后台线程)如果程序中所有前台线程都执行完毕了,后台线程会自动结束,垃圾回收器线程属于守护线程,setDaemon(true) 设置为守护线程

线程同步(多并发造成线程不安全)

当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致
临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性
原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省

同步代码块

synchronizes(临界资源对象){//对临界资源对象加锁
//代码(原子操作)
}
注意:每个对象都有一个互斥锁标记,用来分配给线程的,只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步代码块,线程退出同步代码块时,会释放相应的互斥锁标记,线程退出同步代码块时,会释放相应的互斥所标记

同步方法(非静态方法:锁是this,静态方法:锁是类名.class)

synchronized 返回值类型 方法名称(形参列表){
//对当前对象(this)加锁
//代码(原子操作)
}
注意:只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步方法中,线程退出同步方法时,会释放相应的互斥锁标记

只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记
如调用不包含同步代码块的方法,或普通方法时,则不需要锁标记可直接调用

线程的基本状态

在这里插入图片描述

产生死锁的条件

1.互斥条件:一个资源每次只能被一个进程使用。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

线程通信

等待:public final void wait() 在那等待在那执行,wait之前的代码不在执行
public final void wait(long timeout)
必须在对obj加锁的同步代码块中,在一个线程中,调用obj.wait() 时此线程会释放其拥有的所有锁标记,同时此线程阻塞在0的等待队列中,释放锁,进入等待队列

通知(唤醒):public final void notify() 随机唤醒
public final void notifyAll()
必须在对obj加锁的同步代码块中,从obj的waiting中释放一个或全部线程,对自身没有任何影响

sleep和wait的区别

1 sleep是线程进入休眠状态,wait是线程进入等待队列等待
2 sleep释放了CPU,没释放锁,wait释放了CPU,也释放了锁

线程池

线程容器,可设定线程分配的数量上限,将预先创建的线程对象存入池中,并重用线程池中的线程对象,避免频繁的创建和销毁

获取线程池

常用的线程池接口和类(所在包java.util.concurrent)
Executor:线程池的顶级接口
ExecutorService:线程池接口,可通过submit(Runnable)提交任务代码
Executors工厂类:通过此类可以获得一个线程池

Java四种线程池

1 newFixedThreadPool() 固定大小线程池
2 newCacheThreadPool() 创建动态线程池
3 newSingleThreadExecutor() 单线程池
4 newScheduledThreadPool() 创建调度线程池

关闭线程池:
shutdown() 关闭等待所有的任务执行完毕后,才关闭
shutdownNow() 尝试关闭当前正在执行的线程,关闭所有没有执行的任务

提交任务线程池:
1 延迟执行,只执行一次(用于调度线程池)
es.schedule(runnable,10,TimeUnit.SECONDS)
2 周期执行 ,一秒执行一次,不能关闭线程池
es.scheduleAtFixedRate(runnable,0,1,TimeUnit.SECONDS)
3 固定延迟
es.scheduleWithFixedDelay(runnable,0,1,TimeUnit.SECONDS)

使用ThreadPoolExecutor类创建线程池:
7个参数:
(1)核心线程数 (2)最大线程数 (3)线程存活时间 (4)时间单位
(5 )请求队列 (6)线程创建工厂 (7)拒绝策略
4个拒绝策略:
1 AbortPolicy抛弃任务,并抛出异常,默认的拒绝策略
2 DiscardPolicy放弃任务,没有异常
3 DiscardoldestPolicy放弃老的任务,添加拒绝的任务
4 CallerRunsPolicy线程池的创建线程执行,主线程执行

Callable接口(与线程池一起使用)
public interface Callable<V>{
    public V call() throws Exception;
}

Callable具有泛型返回值,可以声明异常
Callable对象不能直接放入线程中,因此使用FutureTask/Future把可调用对象转成任务之后把任务交给线程,等待任务执行完毕才会继续执行,start开启线程,get获取执行的结果。

Future接口(案例两个线程,一个计算1-50,另一个计算51-100,最后取和)

概念:异步接收ExecytorService.submit()所返回的状态结果
方法:V get() 以阻塞形式等待Future中的异步处理结果(call()的返回值)

Lock接口

1 与synchronized比较,显示定义,结构更灵活
2 提供更多实用性方法,功能更强大,性能更优越

常用方法:
void lock() 获取锁,如锁被占用,则等待
boolean tryLock() 尝试获取锁,成功返回true,失败返回false,不阻塞
void unlock() 释放锁

锁状态

重入锁(嵌套两个锁):
ReentrantLock:lock接口的实现类,与synchronized一样具有互斥锁功能

JDk1.6之后有四种状态:
无锁
偏向锁(只有一个线程)
轻量级锁(两个线程同时在同步代码块,但两者之间互不干扰)
重量级锁

偏向锁和轻量级锁在用户态效率高,重量级锁在内核态效率低
自旋锁是在轻量级锁升级为重量级使用的优化策略

读写锁;
ReentrantReadWritelock:一种支持一写多读的同步锁,读写分离,可分别分配读锁(realock()),写锁(writelock()),支持多次分配读锁,使多个操作可以并发执行

案例:
当写线程有2个,读线程有18个,且线程每次都需要休息1分钟,整个程序运行完,如果用读写锁,只需要3分钟,因为读-读不互斥,写-写互斥,如果不用读写锁则需要20分钟
isTerminated如果所有线程任务完成返回true

互斥规则:
写-----写:互斥,阻塞
读-----写:互斥,读阻塞写,写阻塞读
读-----读:不互斥,不阻塞
在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率

锁消除:JVM配合,开启逃逸分析,锁消除标记,开启标量替换
锁粗化:是将放在循环内部的锁放到循环外,使得只需要执行一次锁就可以

Condition接口(可以创建多个队列,实现线程通信)

1 Condition接口也提供了类似Object的监视器方法,与lock配合可以实现等待/通知模式
2 Condition可以通俗的理解为条件队列,当一个线程在调用了await方法以后,直到线程等待的某个条件为真的时候才被唤醒
await():当前线程进入等待状态
signal():唤醒一个等待线程
signalAll():唤醒所有线程

CAS算法(Compare And Swap 比较交换算法)

1 其实现方式是基于硬件平台的汇编指令,是靠硬件来实现的,效率高
2 并且比较和交换是同步的
3 CAS算法修改的方法包含三个核心参数(V,E,N)V:要更新的变量,E:预期值,N:新值,只有当V==E时,V=N;否则表示已被更新过,则取消当前操作

if(E==V){
    V=N;
}
如V被改变,将改变的值赋值给E,然后再循环一次

CAS乐观锁:
总是以为是线程安全,不怕别的线程修改变量,如果修改了再重新尝试,直到成功
synchronize悲观锁:
总是以为线程不安全,不管什么情况都进行加锁,要是获取锁失败,就阻塞

阻塞队列

ArrayBlockingQueue:数组结构实现,有界队列(手工固定上限)
LinkedBlockingQueue:链表结构实现,无界队列(默认上限Integet.MAX_VALUE)
Queue接口队列先进先出,Collection的子接口,poll():获取第一个元素并移除,peek():获得第一个元素,ConcurrentLinkedQueue<>()线程安全

多线程三个特性(保证并发)

synchronize可保证原子性和可见性,但不能保证有序性
Volatile可保证可见性和禁止指令重排(为了优化,虚拟机会进行重拍,重排对多线程有影响)
Lock接口间接借助了volatile关键字实现了可见性和有序性

原子性:一个或多个操作不能被分割,要么全部执行,要么就都不执行
可见性:多个线程访问同一个变量,一个线程修改了这个变量,别的线程能立即看到修改的值
有序性:程序执行的顺序按照代码的先后顺序执行

面试题:i++不是原子性,循环出现重复数字,解决办法:加锁或原子变量
i++:三个步骤:读,改,写

线程局部变量ThreadLocal

概念:线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java提供ThreadLocal类来支持线程局部变量,是一种实现线程安全的方式。但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。

作用:简单说ThreadLocal就是一种以空间换时间的做法在每个Thread里面维护了一个ThreadLocal.ThreadLocalMap把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了

线程安全的集合

CopyOnWriteArrayList:线程安全的ArrayList,加强版读写分离,先Copy一个副本在添加,写有锁,读 无锁
CopyOnWriteArraySet:线程安全的Set,底层使用CopyOnWriteArrayList实现,使用addifAbsent()添加元素,会遍历数组,不可重复(重复依据equals方法)
ConcurrentHashMap:初始容量默认为16段,使用分段锁设计,不对整个map加锁,而是为了每个Segment加锁,当多个对象存入同一个Segment时,才需要互斥,最理想状态为16个对象分别存入16个Segment,并行数量16,使用方式与HashMap一样,JDK1.8之后改为CAS无锁算法
Collections工具类提供了多个可以获取线程安全集合的方法

public static<T> Collection<T> synchronizedCollection(Collection<T> C)
作用:将不安全线程变成安全线程
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值