一、多线程
流程:程序执行过程,正在运行的程序。作为资源分配的单位
线程:程序内部的执行路径。作为调度和执行的单位,每个线程都有独立的运行栈和程序计数器。
单核CPU:一种假多线程,切换执行
并发:一个CPU使用时间片同时执行对个任务
并行:多个CPU同时执行多个任务
- 程序需要同时执行两个或多个任务
- 程序需要实现用户输入、文件读写操作、网络操作、搜索等一些等待任务
- 当需要一些后台操作程序时
方法一:继承Thread类
- 创造继承Thread类的子类
- 重写Thread类的run()方法
- 重写Thread子类对象
- 调用此对象start()->功能:启动当前线程;调用当前线程run()方法
方法二:实现Runnable接口
- 创建一个实现了Runnable接口的类
- 实现类实现Runnable抽象法:run()
- 创建实现对象
- 将这个对象作为参数传递Thread在类结构中,创建Thread类的对象
- 通过Thread调用类对象start()
比较方法一、二:
- 实现方单继承的局限性
- 实现多线程共享数据的方法更适合处理
方法三:实现Callable接口
比较方法二、三:
call()可有返回值
call()可以抛出异常,通过外部操作捕获异常信息
Callable支持泛型
方法四:使用线程池
- 提供指定线程数量的线程池
- 需要实现指定线程的操作Runnable接口或Callable接口实现类对象
- 关闭连接池
好处:
- 提高响应速度,减少创建新线程的时间
- 减少资源消耗,重复使用线程池中线程,无需每次创建
- 便于线程管理
- corePoolSize:核心池的大小
- maxmumPoolSize:最大线程数
- keepAliveTime:当没有任务时,最多可以停止多长时间
- start():功能:启动当前线程;调用当前线程run()方法
- run():需要重写Thread该方法将创建的线程的操作声明
- currentThread():静态方法,返回执行当前代码的线程
- getName():获取当前线程的名称
- setName():设置当前线程的名字
- yield():释放当前CPU的执行权
- jion():在线程a中调用线程bjoin()方法,此时线程a将进入阻塞状态,直到线程b完全执行,线程a将结束阻塞状态join异常会在方法中抛出,所以我们还需要try-catch捕捉异常处理
- sleep():sleep()方法中的形参long millitime,调用此方法将使当前线程睡眠指定millitime(毫秒),指定millitime在毫秒内,当前线程处于阻塞状态
- isAlive():判断当前线程是否存活,如果当前线程存活,则返回true,若消亡,返回false
获取当前线程的优先级:getPriority() 设置当前线程的优先级:setPriority(int p),p取值范围为1~10 说明:高优先级线程在概率上会有更高的概率抢占低优先级线程cup执行权并不意味着只有高优先线程执行后,才能执行低优先线程。
MAX_PRIORITY:10(最高优先级) MIN_PRIORITY:(最低优先级) NORM_PRIORITY:5(默认优先级)
新建状态(New):Thread t = new MyThread()
就绪状态(Runnable):调用线程对象时start()方法,线程即进入就绪状态。线程处于就绪状态,只是表明线程已经准备好等待CPU调度执行并不意味着执行t.start()此线程将立即执行
运行状态(Running):当CPU当调度线程处于就绪状态时,线程可以真正执行,即进入运行状态
阻塞状态(Blocked):由于某种原因,在运行状态下的线程暂时放弃CPU停止执行使用权,此时进入阻塞状态,直到进入就绪状态,才有机会再次被执行CPU调用进入运行状态。
根据阻塞产生的原因不同,阻塞状态又可以分为三种:
- 等待阻塞:在运行状态下执行线程wait()使线程进入等待阻塞状态的方法
- 同步阻塞:线程正在获取synchronized同步锁失败(因为锁被其他线程占用),会进入同步阻塞状态
- 其他阻塞:通过调用线程的sleep()或join()或发出I/O当要求时,线程将进入阻塞状态。sleep()状态超时,join()等待线程终止或超时I/O处理完毕后,线程再次转移到就绪状态
死亡状态(Dead):线程执行完成或因异常退出run()方法,线程结束生命周期。
方法1:同步代码块
sychronized(同步监视器){///同步代码}
- 操作共享数据的代码,即需要同步的代码
- 共享数据:多线程共同操作的变量
- 同步监视器,俗称锁,任何类别的对象都可以作为锁。多个线程必须共用一把锁。可以考虑使用this充当同步监视器
限制:在操作同步代码时,只能参与一个线程
方法二:同步法
如果在一种方法中完全声明共享数据的代码,则该方法同步sychronized
- 实现同步解决方案Runnable接口的线程安全
- 使用同步方法解决继承问题Thread线程安全问题->加static,保证唯一性
方式三:Lock锁
- 实例化ReentrantLock->private ReentrantLock lock = new ReentrantLock();
- 调用锁定方法lock()
- 调用解锁方法unlock()
不同的线程占用对方需要的同步资源,不放弃,等待对方放弃,形成线程的死锁->所有线程都处于阻塞状态
解决方法:
- 特殊算法、原则
- 尽量减少同步资源的定义
- 尽量避免嵌套同步
wait(),notify(),notifyAll()
wait(),notify(),notifyAll()三种方法的调用者必须是同步代码块或同步方法中使用同步监控器
sleep()和wait()的区别
- 使用方面:
sleep方法是Thread线程方法,而wait是Object顶级类方法。 sleep可在任何地方使用,wait只能用于同步方法和同步块。
- CPU及锁资源释放:
sleep、wait调用后,当前线程将暂停并放弃CPU的执行时间
sleep不会释放当前持有对象的锁资源,到时间后会继续执行
而wait所有的锁都会释放,需要释放notify/notifyAll只有在重新获得对象资源后才能继续执行。
- 异常捕获方面:
sleep需要捕获或抛出异常
而wait/notify/notifyAll则不需要
二、Java常用类
1、String、StringBuffer、StringBuilder
String:不可变的字符系列
StringBuffer:可变字符序列:线程安全,效率低
StringBuilder:可变字符序列:线程不安全,效率高
底层都使用char[]存储
选择:
- 如果你想操作少量的数据 String
- 在多线程操作字符串缓冲区下操作大量数据 StringBuffer
-
单线程操作字符串缓冲区下操作大量数据 StringBuilder
2、比较器
Comparable:
String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象的方法
自定义类需要自己去实现Comparable接口
Comparator:
临时new 一个接口对象,属于临时性的比较
3、枚举类
类的对象只有有限个,确定的
自定义枚举类:
- 声明Season对象的属性:private final修饰
- 私有化类的构造器,并给对象属性赋值
- 提供当前枚举类的多个对象:public static final的
enum关键字枚举类:
- 提供当前枚举类的对象,多个对象之间用","隔开,末尾对象";"结束
- 声明Season对象的属性:private final修饰
- 私有化类的构造器,并给对象属性赋值
- 提供getter/setter方法
4、注解
框架=注解+反射+设计模式
@Annotation,把该Annotation当成一个修饰符使用
- 注解声明为@interface
- 内部定义成员,通常使用value表示
- 可以指定成员的默认值,使用default定义
- 如果自定义注解没有成员,表明是一个标识作用
- 如果注解有成员,在使用注解时,需要指明成员的值
对现有注解进行解释说明的注解
@Target
用于描述注解的范围,即注解在哪用。它说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)等。取值类型(ElementType)有以下几种:
CONSTRUCTOR:用于描述构造器
FIELD:用于描述域即类成员变量
LOCAL_VARIABLE:用于描述局部变量
METHOD:用于描述方法
PACKAGE:用于描述包
PARAMETER:用于描述参数
TYPE:用于描述类、接口(包括注解类型) 或enum声明
TYPE_PARAMETER:1.8版本开始,描述类、接口或enum参数的声明
TYPE_USE:1.8版本开始,描述一种类、接口或enum的使用声明
@Retention
用于描述注解的生命周期,表示需要在什么级别保存该注解,即保留的时间长短。取值类型(RetentionPolicy)有以下几种:
SOURCE:在源文件中有效(即源文件保留)
CLASS:在class文件中有效(即class保留)
RUNTIME:在运行时有效(即运行时保留)
@Documented
用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。它是一个标记注解,没有成员。
@Inherited
用于表示某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
5、集合
集合、数组都是对多个数据进行存储操作的结构,简称java容器
数组(Array):
- 一旦初始化后,其长度就确定了
- 一旦定义好,其元素的类型也就确定了
- 提供的方法有限,对添加删除操作效率不高
- 数据有序可重复,不能满足无序不可重复的需求
集合:
数组、List、Set、Map相互转换:
package com.example.test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ConvertorTest {
/**
* @param args
*/
public static void main(String[] args) {
testList2Array();
testArray2List();
testSet2List();
testList2Set();
testSet2Array();
testArray2Set();
testMap2Set();
testMap2List();
}
private static void testMap2List() {
Map<String, String> map = new HashMap<String, String>();
map.put("A", "ABC");
map.put("K", "KK");
map.put("L", "LV");
// 将Map Key 转化为List
List<String> mapKeyList = new ArrayList<String>(map.keySet());
System.out.println("mapKeyList:"+mapKeyList);
// 将Map Key 转化为List
List<String> mapValuesList = new ArrayList<String>(map.values());
System.out.println("mapValuesList:"+mapValuesList);
}
private static void testMap2Set() {
Map<String, String> map = new HashMap<String, String>();
map.put("A", "ABC");
map.put("K", "KK");
map.put("L", "LV");
// 将Map 的键转化为Set
Set<String> mapKeySet = map.keySet();
System.out.println("mapKeySet:"+mapKeySet);
// 将Map 的值转化为Set
Set<String> mapValuesSet = new HashSet<String>(map.values());
System.out.println("mapValuesSet:"+mapValuesSet);
}
private static void testArray2Set() {
String[] arr = {"AA","BB","DD","CC","BB"};
//数组-->Set
Set<String> set = new HashSet<String>(Arrays.asList(arr));
System.out.println(set);
}
private static void testSet2Array() {
Set<String> set = new HashSet<String>();
set.add("AA");
set.add("BB");
set.add("CC");
String[] arr = new String[set.size()];
//Set-->数组
set.toArray(arr);
System.out.println(Arrays.toString(arr));
}
private static void testList2Set() {
List<String> list = new ArrayList<String>();
list.add("ABC");
list.add("EFG");
list.add("LMN");
list.add("LMN");
//List-->Set
Set<String> listSet = new HashSet<String>(list);
System.out.println(listSet);
}
private static void testSet2List() {
Set<String> set = new HashSet<String>();
set.add("AA");
set.add("BB");
set.add("CC");
//Set --> List
List<String> setList = new ArrayList<String>(set);
System.out.println(setList);
}
private static void testList2Array() {
//List-->数组
List<String> list = new ArrayList<String>();
list.add("AA");
list.add("BB");
list.add("CC");
Object[] objects = list.toArray();//返回Object数组
System.out.println("objects:"+Arrays.toString(objects));
String[] arr = new String[list.size()];
list.toArray(arr);//将转化后的数组放入已经创建好的对象中
System.out.println("strings1:"+Arrays.toString(arr));
}
private static void testArray2List() {
//数组-->List
String[] ss = {"JJ","KK"};
List<String> list1 = Arrays.asList(ss);
List<String> list2 = Arrays.asList("AAA","BBB");
System.out.println(list1);
System.out.println(list2);
}
}
6、泛型
Gneric
在集合中使用泛型
集合接口或集合类在jdk5.0时都修改为带泛型的结构
在实例化集合类时,可以指明具体的泛型类型
7、stream
stream关注的是对数据的运算,与CPU打交道
集合关注的是数据的存储,与内存打交道
- stream自己不会存储元素
- stream不会改变源对象。相反,他们会返回一个持有结果的新stream
- stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
执行流程:
- stream的实例化
- 一系列的中间操作(过滤、映射、……)
- 终止操作
说明:
- 一个中间操作链,对数据源的数据进行处理
- 一旦执行终止操作,就执行中间操作链,并产生结果。之后不会再被使用