LinkedLis:
LinkedList集合特点:线程不安全的类,执行效率高
链接列表结构,查询慢,增删快;
特有功能:
public void addFirst(Object e):在列表开头插入元素
public void addLast(Object e):将元素追加到列表的末尾
public Object getFirst():获取列表的第一个元素
public Object getLast():获取列表的最后一个元素
public Object removeFirst(): 删除列表的第一个元素,并获取第一个元素
public Object removeLast():删除列表的最后一个元素,并获取最后一个元素
public class LinkedListDemo {
public static void main(String[] args) {
//创建一个LinkedList集合对象
LinkedList<String> link = new LinkedList<>() ;
//添加元素
// public void addFirst(Object e):在列表开头插入元素
link.addFirst("hello") ;
link.addFirst("world") ;
link.addFirst("JavaEE") ;
link.addFirst("Android") ;
link.addLast("Php") ;
//public Object getFirst():获取列表的第一个元素
System.out.println(link.getFirst());
System.out.println(link.getLast());
// public Object removeFirst(): 删除列表的第一个元素,并获取第一个元素
System.out.println(link.removeFirst());
System.out.println(link.removeLast());
System.out.println(link);
}
}
Set:
Set集合:无序(存储和取出不一致),
能够保证元素唯一
HashSet:底层数据结构是一个哈希表(桶结构)
线程不安全的类---->不同步---->执行效率高
JDK8以后;提供了juc(并发包:java.util.concurrent):
ConcurrentHashMap<K,V>和HashMap的区别 ?
String类型:String类型本身已经重写了hashCode()和equals,如果hashCode和equals()都相同,
那么认为同一个元素,存储以前的值
public class HashSetDemo {
public static void main(String[] args) {
//创建HashSet集合对象
Set<String> set = new HashSet<>() ;
//添加字符串元素
set.add("hello") ;
set.add("hello") ;
set.add("world") ;
set.add("JavaEE") ;
set.add("JavaEE") ;
set.add("world") ;
set.add("android") ;
set.add("php") ;
set.add("php") ;
set.add(null) ;
System.out.println(set); //元素唯一
}
}
如果现在存储是自定义对象,如何保证元素唯一?HashSet
Student s1 = new Student("高圆圆"42) ;
Student s2 = new Student("高圆圆"42) ;
HashSet集合依赖于add方法---->HashMap的put方法
首先要比较元素的哈希码值相同----->hash()就相同
还要比较成员信息是否相同,对应存储自定的类必须要重写Object的equals方法
Student的这个类,必须手动给出hashCode()和equals
Hashset集合不能保证顺序迭代恒久不变!
应用场景:
在一些需求中,如果没有明确要求元素重复,那就可以使用hashSet,保证元素唯一!
类型:String,Integer,Long,…常用类都已经重写了hashCode和equals方法
public class HashSetDemo2 {
public static void main(String[] args) {
//创建HashSet集合对象
HashSet<Student> hs1 = new HashSet<>() ;
Student s1 = new Student("宋江",35) ;
Student s2 = new Student("宋江",35) ;
Student s3 = new Student("武松",30) ;
Student s4 = new Student("宋江",30) ;
Student s5 = new Student("武松",30) ;
Student s6 = new Student("卢俊义",28) ;
Student s7 = new Student("卢俊义",28) ;
System.out.println("-------------------------------");
//System.out.println(s1.hashCode());
//System.out.println(s2.hashCode());
//添加集合中
hs1.add(s1) ;
hs1.add(s2) ;
hs1.add(s3) ;
hs1.add(s4) ;
hs1.add(s5) ;
hs1.add(s6) ;
hs1.add(s7) ;
//遍历
for(Student s : hs1){
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
public class ArrayListIncludeArrayListTest {
public static void main(String[] args) {
//需要创建一个大的集合
ArrayList<ArrayList<Student>> bigArray = new ArrayList<>() ;
//第一个子集合ArrayList<Student>
ArrayList<Student> firArray = new ArrayList<>() ;
Student s1 = new Student("高圆圆",42) ;
Student s2 = new Student("文章",35) ;
Student s3 = new Student("王宝强",30) ;
firArray.add(s1) ;
firArray.add(s2) ;
firArray.add(s3) ;
//将第一个子集合添加到大集合中
bigArray.add(firArray) ;
//第二个子集合ArrayList<Student>
ArrayList<Student> secArray = new ArrayList<>() ;
Student s4 = new Student("张三",42) ;
Student s5 = new Student("王五",35) ;
Student s6 = new Student("李四",30) ;
secArray.add(s4) ;
secArray.add(s5) ;
secArray.add(s6) ;
//将第二个子集合添加到大集合中
bigArray.add(secArray) ;
//第三个子集合ArrayList<Student>
ArrayList<Student> thirArray = new ArrayList<>() ;
Student s7 = new Student("盲僧",42) ;
Student s8 = new Student("亚索",35) ;
Student s9 = new Student("提莫",30) ;
thirArray.add(s7) ;
thirArray.add(s8) ;
thirArray.add(s9) ;
//将第三个集合添加到集合中
bigArray.add(thirArray) ;
//ArrayList<ArrayList<Student>>遍历大集合
for(ArrayList<Student> array:bigArray){
for(Student s: array){
System.out.println(s.getName()+"\t"+s.getAge());
}
}
}
}
TreeSet:
TreeSet集合:
TreeSet集合 :无序性,元素唯一
底层依赖于TreeMap集合, 红黑树结构(也称为 “自平衡的二叉树结构”),可以实现Map的自然排序以及比较器排序取决于
使用的构造方法
构造方法:
public TreeSet():构造一个空的树,实现元素自然排序 (取决于存储的元素类型能否实现Comparable接口)
自然排序 ----->执行的TreeSet无参构造方法,而且前提条件当前存储类型必须实现Comparable接口
构造构造 新建一个类实现compertor接口;
public class TreeSetDemo {
public static void main(String[] args) {
// public TreeSet()
//Integer类型
TreeSet<Integer> ts = new TreeSet<>() ;
//Intger元素 实现Comparable接口---就能够按照元素自然排序(默认升序排序)
//添加元素
ts.add(20) ;
ts.add(17) ;
ts.add(17) ;
ts.add(18) ;
ts.add(24) ;
ts.add(23) ;
ts.add(24) ;
ts.add(19) ;
//遍历集合TreeSet
for(Integer i:ts){
System.out.println(i);
}
}
}
使用TreeSet存储Student类型,遍历元素
排序条件:
主要条件:按照学生的年龄从小到大排序
自己分析次要条件
public class TreeSetDemo2 {
public static void main(String[] args) {
//创建TreeSet,无参构造方法
TreeSet<Student> ts = new TreeSet<>() ;
//创建几个学生对象
Student s1 = new Student("gaoyuanyuan",42) ;
Student s2 = new Student("gaoyuanyuan",42) ;
Student s3 = new Student("jacky",40) ;
Student s4 = new Student("rose",40) ;
Student s5 = new Student("tomcat",35) ;
Student s6 = new Student("jeffry",35) ;
Student s7 = new Student("liushishi",54) ;
Student s8 = new Student("liudehua",60) ;
//添加
ts.add(s1) ; //因为当前集合存储的自定义对象,元素要实现自然排序必须所在的类实现Compareable接口
ts.add(s2) ;
ts.add(s3) ;
ts.add(s4) ;
ts.add(s5) ;
ts.add(s6) ;
ts.add(s7) ;
ts.add(s8) ;
//遍历
for(Student s : ts){
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
TreeSet能够实现两种排序:
自然排序/比较强排序,取决于构造方法
自然排序:TreeSet(),E类型必须实现Comparable接口,实现自然排序(实现的compareTo(T t))
比较强排序:
public TreeSet(Comparator<? super E> comparator)
Comparator是一个接口类型
1)自定义一个类实现Comparator接口,重写compare方法
2)使用接口的匿名内部类(推荐)
使用TreeSet存储Student类型,遍历元素(使用比较强排序完成)
排序条件:
主要条件:按照学生的年龄从小到大排序
泛型高级通配符(了解)
<?> :任意Java类型,包括Object <? super E> : 向上限定:E类型以及E父类 <? extends E>: 向下限定:E以及它的子类 ```java package com.qf.treeset_05; import java.util.Comparator; import java.util.TreeSet; /** * @author Kuke * @date 2021/8/2 * * TreeSet能够实现两种排序: * 自然排序/比较强排序,取决于构造方法 * 自然排序:TreeSet(),E类型必须实现Comparable接口,实现自然排序(实现的compareTo(T t)) * * 比较强排序: * public TreeSet(Comparator<? super E> comparator) * * Comparator是一个接口类型 * 1)自定义一个类实现Comparator接口,重写compare方法 * 2)使用接口的匿名内部类(推荐) * * * 使用TreeSet存储Student类型,遍历元素(使用比较强排序完成) * 排序条件: * 主要条件:按照学生的年龄从小到大排序 * * * 泛型高级通配符(了解) * <?> :任意Java类型,包括Object- <? super E> : 向上限定:E类型以及E父类
- <? extends E>: 向下限定:E以及它的子类
*/
class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}
public class TreeSetDemo {
public static void main(String[] args) {
/* //创建Collection
Collection<?> c1 = new ArrayList<Object>() ;
Collection<?> c2 = new ArrayList<Animal>() ;
Collection<?> c3 = new ArrayList<Cat>() ;
Collection<?> c4 = new ArrayList<Dog>() ;
System.out.println("------------------------------");
Collection<? super Cat> c5 = new ArrayList<Cat>() ;//最基本的一致
Collection<? super Cat> c6 = new ArrayList<Animal>() ;
Collection<? super Cat> c7 = new ArrayList<Object>() ;
System.out.println("------------------------------");
Collection<? extends Object> c8 = new ArrayList<Object>() ;
Collection<? extends Object> c9 = new ArrayList<Animal>() ;
Collection<? extends Object> c10 = new ArrayList<Cat>() ;
Collection<? extends Animal> c11 = new ArrayList<Animal>() ;
Collection<? extends Animal> c12 = new ArrayList<Cat>() ;
Collection<? extends Animal> c13 = new ArrayList<Dog>() ;*/
// Collection<? extends Animal> c14 = new ArrayList<Object>() ;
//创建TreeSet集合对象
// public TreeSet(Comparator<? super E> comparator)
//MyComparator myComparator = new MyComparator() ; //方式1:接口子实现类对象
//方式2:接口的匿名内部类
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//主要条件:按照学生的年龄从小到大排序
//s1---->就是刚才自然排序里面this
//s2---->就是刚才自然排序里面s
int num = s1.getAge() - s2.getAge() ;
//如果年龄相同,比较姓名是否一样
int num2 = (num==0)? (s1.getName().compareTo(s2.getName())): num ;
return num2;
}
}) ;
//创建几个学生对象
Student s1 = new Student("gaoyuanyuan",42) ;
Student s2 = new Student("gaoyuanyuan",42) ;
Student s3 = new Student("jacky",40) ;
Student s4 = new Student("rose",40) ;
Student s5 = new Student("tomcat",35) ;
Student s6 = new Student("jeffry",35) ;
Student s7 = new Student("liushishi",54) ;
Student s8 = new Student("liudehua",60) ;
ts.add(s1) ;
ts.add(s2) ;
ts.add(s3) ;
ts.add(s4) ;
ts.add(s5) ;
ts.add(s6) ;
ts.add(s7) ;
ts.add(s8) ;
for (Student s:ts) {
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
### map:
ArrayList<Student> 单列集合,只能存一种类型,学生的id,姓名基本信息存储到学生类中
Java提供了双列集合,Map<K,V>:提供一个键值对元素(两种类型),键必须唯一(Map针对键有效,跟值无关)
Map<Integer,String>
id name
1 张三
2 李四
3 王五
4 赵六
1 马七
Map集合:键映射到值的对象,Map集合可以多个值,但键必须唯一!
Map和Collection集合的区别:
Collection:只能存储一种类型 Collection<E> 简单记"光棍"
Map集合:可以两种类型的,键的类型,值的类型 Map<K,V> 简单记"夫妻对"
遍历方式不同
Collection:就通过5种方式(List)
Map:两种方式:
方式1:获取所有的K的集合(键的集合)
通过键获取值
方式2: 获取所有的键值对对象Map.Entry<K,V> ("结婚证")
通过键值对对象获取所有的键("结婚证男方")
通过键值对对象获取所有的值("结婚证女方")
有内在联系:
TreeSet集合---->Collection---->间接的使用到了TreeMap集合的put方法
HashSet阶------>Collection---->间接使用到了HashMap的put方法
Map集合的功能:
V put(K key,V value):添加键值对元素
注意事项:
如果key是第一次添加,那么返回的结果为null
如果key是否重复添加,第二次添加,返回的上一次添加的键对应的值
V remove(Object key):删除指定的键,返回被删除键对应的值
void clear()
boolean containsKey(Object key) :是否包含指定的键 (使用居多)
boolean containsValue(Object value):是否包含指定的值
```java
public class MapDemo {
public static void main(String[] args) {
//创建Map集合:接口
//默认用的是HashMap集合 TreeMap(根据元素排序)
Map<String,String> map = new HashMap<String,String>() ;
System.out.println(map);
//添加功能
// String result = map.put("文章", "马伊琍");
// System.out.println(result);
map.put("文章", "马伊琍");
map.put("王宝强","马蓉") ;
map.put("杨过","小龙女") ;
map.put("郭靖","黄蓉") ;
// String result2 = map.put("文章", "姚笛");
// System.out.println(result2);
map.put("文章", "姚笛");
System.out.println("---------------------------------");
// System.out.println(map.remove("杨过"));
// map.clear();
System.out.println(map.containsKey("周杰伦")) ;
System.out.println(map.containsKey("王宝强")) ;
System.out.println(map.containsValue("郭蓉")) ;
System.out.println(map.containsValue("小龙女")) ;
System.out.println(map);
}
}
高级功能:
Map遍历功能
Set keySet() :获取当前Map集合中的所有的键的集合 (将所有的丈夫集中起来,找对应的妻子)
V get(Object key):通过键获取值
方式2:
获取所有的结婚证 (键值对对象)
Set<Map.Entry<K,V>> entrySet()
通过键值对象 获取键 /获取值(通过结婚证找男方/女方)
K getKey()
V getValue()
public class MapDemo2 {
public static void main(String[] args) {
//创建Map集合对象
Map<String,String> map = new HashMap<>() ;
//添加元素
map.put("令狐冲","东方不败") ;
map.put("杨过","小龙女") ;
map.put("陈玄风","梅超风") ;
map.put("郭靖","黄蓉") ;
// Set<K> keySet() :获取当前Map集合中的所有的键的集合
Set<String> keySet = map.keySet(); //推荐第一种方式
//增强for遍历
for(String key: keySet){
//获取所有的键的元素
// V get(Object key):通过键获取值
String value = map.get(key);
System.out.println(key+"="+value);
}
System.out.println("--------------------------------------");
//方式2:
//Set<Map.Entry<K,V>> entrySet()
Set<Map.Entry<String, String>> entry = map.entrySet();
//增强for:遍历键值对对象获取到
for(Map.Entry<String, String> en: entry){
//获取键和值
//K getKey()
// V getValue()
String key = en.getKey();
String value = en.getValue();
System.out.println(key+"="+value);
}
}
}
HashMap:
Map集合针对键有效:不能迭代顺序恒久不变
HashMap的put方法依赖于hashCode()和equals方法,键的类型必须重写Object类的hashCode和equals方法,保证键唯一!
购物车 (添加,删除,查询,修改商品的属性)---->HashMap进行模拟
选择1---->给购物车中添加商品以及购车编号
选择2---->删除购物车的商品
HashMap<Product,Integer>
Product
Integer id ;//商品编号
String name ;//商品名称
Integer price ;//商品价格
String desc ;//商品描述
HashMap<Student,String>: HashMap<String,Student>
Key:Student类型(姓名和年龄):自定义类型
Value:String(爱好)
public class HashMapDemo {
public static void main(String[] args) {
//创建Map集合对象
HashMap<Student,String> map = new HashMap<>() ;
//创建几个学生对象
Student s1 = new Student("文章",35) ;
Student s2 = new Student("文章",35) ;
Student s3 = new Student("文章",37) ;
Student s4 = new Student("潘玮柏",40) ;
Student s5 = new Student("赵又廷",39) ;
Student s6 = new Student("蔡徐坤",38) ;
Student s7 = new Student("蔡徐坤",38) ;
Student s8 = new Student("肖战",30) ;
map.put(s1,"足球") ;
map.put(s2,"篮球") ;
map.put(s3,"足球") ;
map.put(s4,"吸毒") ;
map.put(s5,"高圆圆") ;
map.put(s6,"乒乓球") ;
map.put(s7,"篮球") ;
map.put(s8,"演戏") ;
//遍历
Set<Student> students = map.keySet();
for(Student key :students){
//通过键获取值
String hobit = map.get(key);
System.out.println(key.getName()+"---"+key.getAge()+"---"+hobit);
}
}
}
TreeMap:
TreeMap:红黑树结构—针对Map的键按照条件排序—键属于自定义的情况
TreeMap<Integer,String>
TreeMap<Student,String>
存储学生类型(姓名,年龄) ,value:描述"朝代"
键必须唯一而且排序的主要条件:按照学生的年龄从小到大排序
TreeMap的构造方法
public TreeMap():针对键进行自然排序
public TreeMap(Comparator<? super K> comparator):针对键按照比较器进行排序
TreeSet
键盘录入的学生姓名,语文,英语,数学,总分高到底
Student
private String name
private int chinese
private english
private math
public int getSum(){
return this.chinese+this.engish+math;
}
TreeSet集合对象(比较器排序)
//创建键盘录入对象
键盘录入
for(int x = 1 ; x <=5 ; x ++){
//全部用String
//提示请输入第 i 个 学生的姓名
String name = sc.nextLine() ;
//语文,数学,英语
String chineseStr = sc.nextLine();
String englisStr = sc.nextLine();
String mathStr = sc.nextLine();
//将String—>int
int chinese = Integer.parseInt(chineseStr) ;
…
Student s = new Student() ;
//封装数据
s.setName(name) ;
s.setChinese(chinese) ;
s.setEnglish(english) ;
s.setMath(math) ;
将学生添加TreeSet集合中
ts.add(s) ;
}
public class TreeMapDemo {
public static void main(String[] args) {
//创建TreeMap集合对象
// TreeMap<Student,String> tm = new TreeMap<>() ; //无参构造方法:自然排序 :前提条件:键 的类型必须实现Comparable
//比较器排序:匿名内部类
TreeMap<Student,String> tm = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//主要条件:学生的年龄从大到小排序
int num = s2.getAge() - s1.getAge() ;
//如果年龄相同,要比较姓名的内容是否相同
int num2 = (num==0)? (s1.getName().compareTo(s2.getName())):num ;
return num2;
}
}) ;
//创建学生对象
Student s1 = new Student("唐伯虎",38) ;
Student s2 = new Student("唐伯虎",38) ;
Student s3 = new Student("秋香",30) ;
Student s4 = new Student("祝枝山",40) ;
Student s5 = new Student("祝枝山",45) ;
Student s6 = new Student("文征明",39) ;
Student s7 = new Student("石榴姐",20) ;
Student s8 = new Student("东香",18) ;
Student s9 = new Student("徐香",18) ;
tm.put(s1,"明朝") ;
tm.put(s2,"宋代") ;
tm.put(s3,"清朝") ;
tm.put(s4,"明朝") ;
tm.put(s5,"现代") ;
tm.put(s6,"唐朝") ;
tm.put(s7,"宋代") ;
tm.put(s8,"明朝") ;
tm.put(s9,"现代") ;
Set<Student> students = tm.keySet();
for(Student key :students){
String value = tm.get(key);
System.out.println(key.getName()+"---"+key.getAge()+"---"+value);
}
}
}
二分法遍历:
数组高级查找算法之二分搜索算法
前提条件数组必须有序,如果数组本身无序,让我们查询元素,先排序再去查找!(有条件需求先排序,再查
如果没有条件需求,只能使用基本元素查找法:从头查找到尾)
如果需求明确要求手动书写,直接可以使用工具类即可!
Arrays工具类:
里面提供排序sort(任何数组进行排序) :元素升序排序 Integer,String
里面提供二分搜素法(任何类型进行查询 ,int key)
public class BinarySearch {
public static void main(String[] args) {
//已知数组,静态初始化
int[] arr = {11,22,33,44,55} ;
//调用二分搜索方法查询
int index = binarySearch(arr, 22);
System.out.println(index);
int index2 = binarySearch(arr,66) ;
System.out.println(index2);
int index3 = binarySearch(arr,44) ;
System.out.println(index3);
System.out.println("-----------------------------------");
int index4 = Arrays.binarySearch(arr, 55);
System.out.println(index4);
int index5 = Arrays.binarySearch(arr, 66);
System.out.println(index5);
}
//返回值int
//方法参数:数组,查询的元素
public static int binarySearch(int[] arr,int target){
//防止空指针异常
if(arr!=null){
//定义数组的最小索引:
int min = 0 ;
//定义最大索引
int max = arr.length -1 ;
//使用循环while
while(min<=max){
//计算中位点索引
int mid = (min+max)/2 ;
//如果当前中位点对应的元素小于要要查找的元素
if(target < arr[mid]){
//左半区域:继续折半
max = mid -1 ;
}else if(target > arr[mid]){
//右边区域:继续折半
min = mid + 1 ;
}else{
//查询到了
return mid ;
}
}
}
//循环结束之后,还没有找,则返回-1
return -1 ;
}
}
Collections:针对集合操作工具类
提供静态功能:
public static <T extends Comparable<? super T>> void sort(List list):按照自然升序排序(针对List集合排序)
public static void sort(List list,Comparator<? super T> c):按照比较器排序针对List集合
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>:获取当前自然顺序中List的最大值
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T>:最小值
public static void reverse(List<?> list):对List集合顺序反转
public static void shuffle(List<?> list):随机置换
public class CollectionsDemo1 {
public static void main(String[] args) {
//创建List集合
List<Integer> list = new ArrayList<>() ;
//添加元素
list.add(10) ;
list.add(50) ;
list.add(15) ;
list.add(25) ;
list.add(5) ;
list.add(12) ;
System.out.println(list);
System.out.println("---------------------------------");
//public static <T extends Comparable<? super T>> void sort(List<T> list):
Collections.sort(list);
System.out.println(list);
System.out.println("----------------------------------");
//public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>
Integer max = Collections.max(list);
System.out.println(max);
System.out.println("----------------------------------");
System.out.println(Collections.min(list));
System.out.println("-----------------------------------");
Collections.reverse(list);//反转
System.out.println(list);
System.out.println("------------------------------------");
// public static void shuffle(List<?> list):随机置换
Collections.shuffle(list);
System.out.println(list);
}
}
public static <T extends Comparable<? super T>> void sort(List list):按照自然升序排序(针对List集合排序)
public static void sort(List list,Comparator<? super T> c):按照比较器排序针对List集合
List:针对List进行自然升序排序,
主要条件:按照学生的年龄从小到大排
次要条件:年龄相同,比较姓名内容是否相同!
public class CollectionsTest {
public static void main(String[] args) {
//创建List集合对象
List<Student> list = new ArrayList<>() ;
//创建几个学生对象
Student s1 = new Student("gaogaoyuan",42) ;
Student s2 = new Student("gaogaoyuan",40) ;
Student s3 = new Student("liushishi",42) ;
Student s4 = new Student("wanglihong",45) ;
Student s5 = new Student("wenzhang",38) ;
Student s6 = new Student("huazi",35) ;
Student s7 = new Student("huazi",32) ;
Student s8 = new Student("zhangjunjie",20) ;
//添加
list.add(s1) ;
list.add(s2) ;
list.add(s3) ;
list.add(s4) ;
list.add(s5) ;
list.add(s6) ;
list.add(s7) ;
list.add(s8) ;
//排序
//Collections.sort(list); //自然升序排序:针对集合当前存储的类型必须实现Comparable
//使用比较器排序:针对List集合
//public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge() ;
int num2 = (num==0)?(s1.getName().compareTo(s2.getName())):num ;
return num2;
}
});
for(Student s:list){
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
扑克牌游戏两种方式;
package com.qf.test_02;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
/**
* 使用集合模拟都地主洗牌和发牌,看牌,每个手上牌无序
*
* 没有告诉什么集合,默认使用ArrayList<String>
*
* 1)创建一个牌盒 :ArrayList<String>
*
* 2)装牌
* 需要有点数和花色数组中进行嵌套遍历----> 所有元素拼接起来,装到牌盒中
* 还需要给ArrayList里面添加”小王“,“大王“
*
* 3)洗牌:就是将ArrayList集合中的所有元素随机置换了
* public static void shuffle(List<?> list):随机置换
*
* 4)发牌
* 三个人,A,B,C
* A-->B--->C --->A
*
* 三个人都分别是ArrayList
* 创建一个集合:diPai
* 判断:
* 如果角标>牌盒整个size()-3 底牌
* 如果角标 %3 == 0 第一个人的
* 如果角标 %3 == 1 第二个人的
* %3 == 2 第三个人的
*
* 5)看牌:每个人都可以牌,还可以看底牌
* 封装一个功能lookPoker(String name,ArrayList<String> array)
*
*/
public class PokerTest {
public static void main(String[] args) {
//1)创建牌盒 ArrayList
//花色和点数--->拼接起来 String(牌)
ArrayList<String> array = new ArrayList<>() ;
//2)装牌
//2.1)创建一个花色数组
String[] colors = {"♥","♠","♣","♦"} ;
//2.2)创建一个点数数组
String[] numbers = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"} ;
//2.3)点数和花色拼接
for(String number:numbers ){
for(String color :colors){
//number拼接color
String poker = number.concat(color);
//将牌添加到牌盒中
array.add(poker) ;
}
}
//添加小王
array.add("小王") ;
//添加大王
array.add("大王") ;
//System.out.println(array);
//3)洗牌
//随机置换
Collections.shuffle(array);
// System.out.println(array);
//4)发牌
/*
* 三个人都分别是ArrayList
* 创建一个集合:diPai
* 判断:
* 如果角标>牌盒整个size()-3 底牌
* 如果角标 %3 == 0 第一个人的
* 如果角标 %3 == 1 第二个人的
* %3 == 2 第三个人的
*/
//三个人都是集合ArrayList
ArrayList<String> plary1 = new ArrayList<>() ;
ArrayList<String> plary2 = new ArrayList<>() ;
ArrayList<String> plary3 = new ArrayList<>() ;
//底牌---->看成集合
ArrayList<String> diPai = new ArrayList<>() ;
//遍历牌盒array:获取每张牌
for(int x = 0 ; x < array.size() ; x ++){//x角标 0 开始 0 ---53
//判断
if(x >= array.size()-3 ){
//底牌
diPai.add(array.get(x)) ;
//三个人玩
}else if(x % 3 == 0){
//第一个人的
plary1.add(array.get(x)) ;
}else if(x % 3 == 1){
//第二个人
plary2.add(array.get(x)) ;
}else if(x % 3 == 2){
//第三个人
plary3.add(array.get(x)) ;
}
}
//看牌:每一个人都可以看牌,还可以看底牌,所以看牌封装一个功能
lookPoker("张俊杰",plary1);
lookPoker("吴亦凡",plary2);
lookPoker("都美竹",plary3);
lookPoker("底牌",diPai);
}
//看牌的功能
public static void lookPoker(String name,ArrayList<String> list){
//张三的牌是: xxx,xx,xx
//李四的牌是: xxx,xx,xx
System.out.print(name+"的牌是:");
//遍历ArrayList集合
for(String poker: list){
System.out.print(poker+" ");
}
System.out.println();
}
}
有序:
package com.qf.test_02;
import java.util.*;
/**
* @author Kuke
* @date 2021/8/3
*
* 模拟斗地主,保证牌有序
*/
public class PokerTest2 {
public static void main(String[] args) {
//1)牌盒
//创建一个牌盒Map:HashMap<Integer,String> key:编号 value:牌
HashMap<Integer,String> hm = new HashMap<>();
//创建一个ArrayList集合:存储编号
ArrayList<Integer> arrayList = new ArrayList<>() ;
//2)装牌
//创建点数数组
String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"} ;
//创建花色数组
String[] colors = {"♥","♠","♣","♦"} ;
//拼接
//定义牌的编号:0开始
int index = 0 ;
for(String number:numbers){
for(String color:colors){
String porker = number.concat(color);
//将编号以及牌都添加HashMap集合
hm.put(index,porker) ;
//单独给ArrayList存储编号
arrayList.add(index) ;
index ++ ;
}
}
//给HashMap集合添加小王,大王,给ArrayList添加小王,大王的编号
hm.put(index,"小王") ;
arrayList.add(index) ;
index ++ ;
hm.put(index,"大王") ;
arrayList.add(index) ;
// System.out.println(arrayList);
//3)洗牌:随机置换 ArrayList<Integer> 洗的是编号
Collections.shuffle(arrayList);
// System.out.println(arrayList);
//4)发牌
/*
为了保证牌有序,发也是编号
* 三个人都分别是 TreeSet<Integer>集合
* 创建一个集合:diPai
* 判断:
* 如果角标>=牌盒整个size()-3 底牌
* 如果角标 %3 == 0 第一个人的
* 如果角标 %3 == 1 第二个人的
* %3 == 2 第三个人的
*/
TreeSet<Integer> player1 = new TreeSet<>() ;
TreeSet<Integer> player2 = new TreeSet<>() ;
TreeSet<Integer> player3 = new TreeSet<>() ;
//底牌
TreeSet<Integer> diPai = new TreeSet<>() ;
for(int x = 0 ;x < arrayList.size() ; x ++){
//0开始
if(x >= arrayList.size()-3){
diPai.add(arrayList.get(x)) ;
}else if(x % 3 == 0 ){
player1.add(arrayList.get(x)) ;
}else if(x % 3 == 1){
player2.add(arrayList.get(x)) ;
}else if(x % 3 ==2){
player3.add(arrayList.get(x)) ;
}
}
//看牌://看牌:每一个人都可以看牌,还可以看底牌,所以看牌封装一个功能
lookPoker("张俊杰",player1,hm);
lookPoker("高圆圆",player2,hm);
lookPoker("赵又廷",player3,hm);
lookPoker("底牌",diPai,hm);
}
public static void lookPoker(String name,TreeSet<Integer> ts,HashMap<Integer,String> hm){
//玩家1的牌是:xxx...
//玩家2的牌是:xxx....
System.out.print(name+"的牌是:");
//遍历TreeSet集合,获取每一个编号
for(Integer key: ts){
//获取到每一个编号---在HashMap集合中属于key(键) 编号
String poker = hm.get(key); //在大Map集合中通过键获取值
System.out.print(poker+" ");
}
System.out.println();
}
}
System:
成员变量:
public static final InputStream in:标准输入流
public static final PrintStream out:标准输出流
public static final PrintStream err:错误输出流(打印错误信息/一些信息需要用户引起注意:相关的日志)
后期使用日志文件:log4j.properites
级别
info:详情信息
debug:断点调试模式
error:错误信息
warning:警告信息
System.exit(0) :jvm退出 ,0表示正常终止
public static void gc():手动开启垃圾回收器,会去回收内存中没有更多引用的对象!
public class SystemDemo {
public static void main(String[] args) throws IOException {
// System.err.println("这是错误输出流");
InputStream in = System.in ; //字节输出流
//键盘录入的就可以用到Scanner / BufferedRead 字符缓冲输入流:读数据
Scanner sc = new Scanner(in) ; //里面传递参数InputStream参数
System.out.println("helloworld");
System.out.println("--------------------------------------");
PrintStream ps = System.out ; //字节打印流
//println(任何数据类型):PrintStream流中的一些工
ps.println(10);
ps.println(12.56);
System.out.println("----------------------------------------");
//创建Person对象
Person p = new Person("高圆圆",41) ;
System.out.println(p.getName()+"---"+p.getAge());
p = null ;
//手动垃圾回收器(不需要手动开启垃圾回收器,自动回收)!
System.gc() ;
//protected void finalize() throws Throwable:需要通过jvm启动垃圾回收器回收没有更多引用的对象
/*int num = Runtime.getRuntime().availableProcessors();
System.out.println(num);*/
// Runtime.getRuntime().exec("notepad") ;
// Runtime.getRuntime().exec("calc") ;
}
}
异常:
异常:
Throwable:包含所有的错误以及异常! 它是一个超类(父类)
E error , Exception
error:非常严重问题 (跟代码没有太大有关系)
OOM Out Of Memory:内存溢出 (严重问题)
举例:
手机移动端 (旅游app)
下拉刷新的速度比图片还快(一个activity:界面 一次性刷新很多图片)
Exception:异常
编译时期异常和运行时期异常(RuntimeException):程序在运行过程中出现问题(代码书写不严谨)
只要不是RuntimeException的子类都是属于编译时期异常
error----> 在生活中 “地震了,不可抗力的因素”
Exception:异常
编译时期异常: 在程序,运行前需要检查的! 在生活中 “长途旅行之前,检查你的车胎情况”…
运行时期异常:在程序.程序代码逻辑问题(代码不严谨) 在生活中 “no zuo no die”
异常的处理两种方式:
标准格式:try…catch…finally
变形格式
try{
//可能出现问题的代码
}catch(异常类名 变量名){
//处理异常
}
try{
//可能出现问题的代码
}catch(异常类名 变量名){
//处理异常1
}catch(异常类名 变量名){
//处理异常2
}
//多线程:jdk5以后:Lock:接口 (锁:可重复入的互斥锁)
try{
//可能出现问题的代码
}finally{
//释放资源(系统资源)
}
throws:抛出
public class ExceptionDemo1 {
public static void main(String[] args) {
//定义两个变量
/* int a = 10 ;
int b = 0 ;
System.out.println(a/b); //jvm在内存中会创建当前异常类的对象 new ArithmeticException()*/
//使用try...catch进行捕获异常
try{
//可能出现问题代码
int a = 10 ;
int b = 0 ; //直接获取到的,以后可能值---->通过一些方法获取到的值
System.out.println(a/b);
System.out.println("over");
}catch(ArithmeticException e){ //捕获异常:可以使用大的Exception,但是捕获:具体异常具体捕获
System.out.println("除数不能为0");
}
//....
}
}
使用try…catch去处理多个异常:
public class ExceptionDemo2 {
public static void main(String[] args) {
//method1() ;
// method2() ;
//method3() ;
method4() ;
}
/*
jdk7以后提供的 try{
可能出现问题的代码
}catch(异常类名1 | 异常类名2 | 异常类名3 ...变量名){ //异常类名必须为同级别
处理异常
}
*/
private static void method4() {
try {
int a = 10;
int b = 0;
int[] arr = {4, 5, 6};
System.out.println(a / b);
System.out.println(arr[0]);
System.out.println(arr[3]);
} catch (ArithmeticException | ArrayIndexOutOfBoundsException e) {//包含了所有 编译时期/运行时期异常
System.out.println("程序出问题了...");
}
}
//使用trt...catch...catch...catch...不能将大的异常放在最前面
private static void method3() {
try {
int a = 10;
int b = 1; //可能为0
int[] arr = {4, 5, 6};
arr = null; //空值
System.out.println(a / b);
System.out.println(arr[0]);
System.out.println(arr[3]);
} catch (Exception e) {//包含了所有 编译时期/运行时期异常
System.out.println("程序出问题了...");
}
/* }catch (ArrayIndexOutOfBoundsException e){
System.out.println("访问了数组中不存在的索引");
}catch(ArithmeticException e){// 通过jvm创建当前异常类对象,如果当前e类型匹配就会catch语句
System.out.println("除数不能为0");
}*/
}
//针对多个异常进统一处理,try...catch...catch
private static void method2() {
try{
int a = 10;
int b = 1 ; //可能为0
int[] arr = {4,5,6} ;
arr = null ; //空值
System.out.println(a/b);
System.out.println(arr[0]);
System.out.println(arr[3]);
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("访问了数组中不存在的索引");
}catch(ArithmeticException e){// 通过jvm创建当前异常类对象,如果当前e类型匹配就会catch语句
System.out.println("除数不能为0");
}catch (Exception e){
System.out.println("程序出问题了...");
}
}
//分别针对可能出现问题的代码,进行try...catch(不推荐)
private static void method1() {
try{
int a = 10;
int b = 0 ;
System.out.println(a/b);
}catch (ArithmeticException e){
System.out.println("除数不能为0");
}
//创建一个数组
try{
int[] arr = {4,5,6} ;
System.out.println(arr[0]);
System.out.println(arr[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("访问了数组中不存在索引");
}
}
}
编译时期异常和运行时期异常的区别?
RuntimeException:运行时期异常
很多的子类:NullPointerException,ClassCastException,ArrayIndexOutOfBoundsException…
运行时期异常:
一般程序员逻辑结构不严谨导致的问题,调用者可以进行显示处理(try…catch…/throws)
也可以不进行显示处理,通过逻辑语句进行处理!
编译时期异常:调用者必须显示处理,不处理,编译通过不了,程序运行不了
如果在当前方法中已经去捕获了try…catch…,调用者无序进行处理,
但是如果在方法中抛出异常的,调用者必须处理(捕获/抛出throws)
开发中:
尽量优先使用try…catch异常,其次再是throws
public class ExceptionDemo3 {
public static void main(String[] args) throws Exception {
method1() ;//调用者
/* try {
method2() ;
} catch (ParseException e) {
System.out.println("解析出问题了...");
}*/
method2();
}
//编译时期异常
private static void method2() throws ParseException,ClassNotFoundException,Exception {
//String日期文本--->java.util.Date日期对象(解析)
//捕获异常.调用者不会进行处理
/* try{
String s = "2021-8-3" ;
//创建SimpleDateFormat对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
Date dat = sdf.parse(s) ; //parse方法本身就有一个异常,throws ParseException
}catch (ParseException e){
System.out.println("解析出问题了...");
}*/
//在当前方法中使用throws
String s = "2021-8-3" ;
//创建SimpleDateFormat对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd ") ;
Date dat = sdf.parse(s) ;
}
//运行时期异常
private static void method1() {
//显示处理了 (能使用异常处理方式:捕获/抛出throws,就是用他)
/* try{
int a = 10 ;
int b = 0 ;
System.out.println(a/b);
}catch (Exception e){
System.out.println("除数为0了...");
}*/
//并没有做显示处理
int a = 10 ;
int b = 0 ;
if(b!=0){
System.out.println(a/b);
}else{
System.out.println("除数为0了...");
}
}
}
1)有的时候没有办法去抛出,继承关系中,如果子类继承父类,
父类的该方法没有异常,子类重写该方法的时候,只能try…catch
2)子类继承父类,如果父类的该方法本身抛出异常了,那么子类重写该方法的时候,要么跟父类的方法的异常类名一致,要么是该异常的类子类!
public class ExceptionDemo4 {
public static void main(String[] args) {
}
}
class Father{
public void show(){}
public void method() throws ArrayIndexOutOfBoundsException{}
}
class Son extends Father{
/* @Override
public void method() throws Exception { //要么跟他父类的方法异常类名一致,要么是异常类名的子类
}*/
@Override
public void method() throws ArrayIndexOutOfBoundsException { //要么跟他父类的方法异常类名一致,要么是异常类名的子类
}
public void show() {
//将String--->Date
try{
String source = "2021-8-3" ;
//创建SimpleDateFormat对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
//解析
Date date = sdf.parse(source) ;
System.out.println(date);
}catch (ParseException e){
System.out.println("解析出问题了...");
}
}
}
面试题:
throws和throw的区别?
共同点:都是抛出
用法不同:
1)使用位置不同
throws:
a)将异常抛出在方法声明上
b)在方法名的后面可以跟多个异常类名,中间逗号隔开!
throw
a)在方法的语句体中某个逻辑语句中
b)它后面只能跟异常对象,而不是类名
2)调用者是否处理不同
throws:调用者必须进行显示处理(try…catch/throws),否则报错
throw:调用者无须显示处理,一般情况都是在逻辑语句进行处理
3)出现异常是否肯定性
throws:在方法上的,执行某个方法的代码中,可能有问题(表示出现异常的一种可能性)
throw:执行某段代码一定会执行这个异常(表示出现异常的一种肯定性)
throws---->将具体的处理交给jvm—通过jvm吧异常信息打印控制台上
显示的底层源码而且会显示当前错误消息字符串
throw---->程序 中某段代码有问题:只是打印异常类名(jvm处理)
public class ExceptionDemo5 {
public static void main(String[] args) throws ParseException {
//
// try {
// method();
// } catch (ParseException e) {
// System.out.println("解析问题了...");
// }
method();
// method2();
}
//throw
private static void method2() throws ArithmeticException {
int a = 10 ;
int b = 0 ;
//逻辑语句
if(b!=0){
System.out.println(a/b);
}else{
//抛出一个对象
//throw 匿名对象 new XXXException() ;
throw new ArithmeticException() ;
}
}
//throws
private static void method() throws ParseException {
//将String--->Date
String source = "2021-8-3" ;
//创建SimpleDateFormat对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd ") ;
//解析
Date date = sdf.parse(source) ;
System.out.println(date);
}
}
不管使用throws/try…catch…finally:
都是要通过jvm调用Throwable里面的功能完成日志(错误信息)打印
try{
//可能出现问题的代码 //首先加载这些代码 (语法校验)
}catch(异常类名 变量名){ //如果存在问题,那么jvm在内存中创建异常的实例,实例是否为catch语句中类型的实例
//类似于 a instanceOf 引用类型:如果是就会执行catch语句
异常处理 //要么手动处理/要么通过jvm进行处理(打印错误日志信息)
}finally{
释放资源…
}
Exception----->Throwable
public String getMessage()获取详细消息字符串 同义的方法:public String getLocalizedMessage()
public String toString():打印出当前异常信息的简短描述:
内存中异常对象的类名:(全限定名称:包名.类名) 换行
“:”(一个冒号和一个空格) 换行
public String getMessage():消息字符串
//public void printStackTrace():跟踪堆栈:包含toString以及底层原码(某行代码出现问题)以及本类中的代码问题
都是体现方法上(将当前跟方法相关的底层方法全部跟踪一遍)
finally:不能单独使用,它是结合try…catch…finally:异常的标准格式
finally用法:
特点:
释放相关的资源,代码一定执行的
除非在执行finally,jvm退出了!
public class ExceptionDemo6 {
public static void main(String[] args) {
try{
String source = "2021-8-3" ;
//创建SimpleDateFormat对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd ") ;
//解析
Date date = sdf.parse(source) ;
System.out.println(date);
}catch (ParseException e){
//使用Throwabl里面的功能
//String result = e.getMessage();//获取消息字符串
// System.out.println(result);
// String result = e.toString();
// System.out.println(result);
e.printStackTrace();
System.exit(0);
}finally{
//finally语句代码是一定执行的!
//释放资源
System.out.println("需要释放当前系统资源"); //jdbc( Connection连接对象/执行对象Statement)
//IO流(创建流对象,流对象得释放----系统资源)
//框架里面:Mybatis(JDBC的封装): 执行对象SqlSession
}
//快捷键:针对try---catch---finally: alt+ctrl+t--->try...catch:捕获
}
}
面试题:
如果在某个方法中捕获异常,
但是该方法有返回值类型,如果在catch语句出现return语句,finally代码还会执行吗?
如果会执行,在return前还是在后
finally会执行,但是现在这个代码,在catch语句已经形成返回路径,它会记录最终返回就是30;finally是去释放资源用的,
很少牵扯业务代码;都会执行的,除非jvm退出!
public class Test {
public static void main(String[] args) {
int num = getNum(10) ;// i=10
System.out.println(num);
}
private static int getNum(int i) {
try{
i = 20 ; //i = 20 ;
System.out.println(i/0); //除数为0
}catch (ArithmeticException e){
i = 30 ; //i =30
return i ; // return i = return 30 :已经在catch语句形成返回的路径 返回结果就是30
}finally { //finally代码一定会执行,除非jvm退出了
i = 40 ; // i = 40
// return i ;
}
return i; //30
}
}
线程:
线程模拟线程环境
开启两条线程
创建线程的实现 方式1:
1)将一个类声明为Thread的子类
- 这个子类应该重写Thread类的run方法
3)然后可以分配并启动子类的实例。
启动线程用的start()而不是run()
run()只是一个普通方法,不会出现线程的执行具有随机性(不会互相抢占cpu执行权)
不会出现两个线程并发执行
public class ThreadDemo {
public static void main(String[] args) {
//3)创建Thread类的子类对象
MyThread my1 = new MyThread() ;//第一个线程对象
MyThread my2 = new MyThread() ; //第二个线程对象
//4)启动
//my1.run();
//my2.run();
/* my1.start();
my1.start();
my1只是代表一个线程对象
my1将start方法调用两次---->IllegalThreadStateException:非法线程状态异常
start()原码: 校验当前线程状态:如果线程已经启动了,就不能再启动
*/
my1.start();//start():有jvm调用底层run方法,出现并发执行
my2.start();
}
}
Thread类的构造方法:
Thread(String name):创建线程类对象,设置名称
Thread类的成员方法
public final String getName():获取线程名称
public final void setName(String name):设置线程名称
线程的优先级
Thread类中静态常量字段(成员变量field)
public static final int MAX_PRIORITY 10 最大优先级
public static final int MIN_PRIORITY 1 最小优先级
public static final int NORM_PRIORITY 5 默认优先级
public final void setPriority(int newPriority):设置线程的优先级
public final int getPriority():获取优先级
优先级越大的:抢占CPU的执行权越大
优先级小的:抢占CPU的执行权越小
默认优先级:随机性大一些
public class ThreadDemo2 {
public static void main(String[] args) {//用户线程
//创建MyThread2类的对象
MyThread2 t1 = new MyThread2() ;
MyThread2 t2 = new MyThread2() ;
MyThread2 t3 = new MyThread2() ;
// public final void setName(String name):设置线程名称
t1.setName("洪学佳") ;
t2.setName("张俊杰") ;
t3.setName("高圆圆");
t1.setPriority(10); //最大优先级
t2.setPriority(1);//最小优先级
int num1 = t1.getPriority();
int num2 = t2.getPriority();
int num3 = t3.getPriority();
System.out.println(num1+"---"+num2+"---"+num3);
//启动线程
t1.start();
t2.start();
t3.start();
}
}
守护线程:
public final void setDaemon(boolean on)
参数为true,表示标记当前线程为守护线程,当正在运行的线程如果都是守护线程,则jvm自动退出
这个方法必须在启动线程之前调用(start()之前)
举例:
玩游戏:坦克大战
这个两个坦克----守护线程
如果运行的线程都是守护线程,jvm退出,运行的线程不会立即停止掉!
public class ThreadDaemonDemo {
public static void main(String[] args) {
//创建两个线程
ThreadDaemon td1 = new ThreadDaemon() ;
ThreadDaemon td2 = new ThreadDaemon() ;
//设置名称
td1.setName("张飞");
td2.setName("关羽");
//设置为守护线程
td1.setDaemon(true) ;
td2.setDaemon(true) ;
//启动线程
td1.start();
td2.start();
// public static Thread currentThread():获取正在运行的线程执行对象的引用
Thread.currentThread().setName("刘备");
//提供for循环:
for(int x = 0 ; x < 5 ; x ++){
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
等待该线程终止:
public final void join() throws InterruptedException:等待该线程终止!
底层依赖于线程安全的方法join(0):永远等待(当前执行完毕结束!)
又依赖于wait(long time)
考虑安全问题:
三个线程—优先级 都是默认(5)
都去设置join(): 三个线程谁先抢占到谁先执行
public class ThreadJoinDemo {
public static void main(String[] args) {
//创建三个线程
JoinThread jt1 = new JoinThread() ;
JoinThread jt2 = new JoinThread() ;
JoinThread jt3 = new JoinThread() ;
//设置线程名称
jt1.setName("李渊") ;
jt2.setName("李世民") ;
jt3.setName("李元霸") ;
//启动线程
jt1.start();
//jt1调用join
try {
jt1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
jt3.start();
}
}
暂停:
public static void yield():暂停当前正在执行的线程,执行对方线程
public class ThreadYieldDemo {
public static void main(String[] args) {
//创建两条线程对象
YieldThread yt1 = new YieldThread() ;
YieldThread yt2 = new YieldThread() ;
//设置名称
yt1.setName("高圆圆") ;
yt2.setName("赵又廷") ;
//启动线程
yt1.start();
yt2.start();
}
}
练习题:
package com.qf.thread_06;
/**
* @author Kuke
* @date 2021/8/4
* 电影院有三个窗口,共同出售100张票,使用多线程创建方式1来进行实现!
*
* 分析
* 1)自定义类SellTicket extends Thread
* tickets:票 = 100张;
* 2)重写run方法
* 耗时的操作 :模拟一致有票,使用while(true)
* 3)在main用户(主线程)创建三个 sellTicket 对象
* 4)分别设置线程名称:窗口1,窗口2,窗口3
* 5)分别启动线程
*
*
* 方式1 多线程的创建方式存在弊端
* 1)它是一个继承关系, 具有"局限性"
* 2)不能够体现资源共享的概念----- 因为Thread类使用到的静态代理 (设计模式)
*
* st1,st2,st3 :三个栈内存变量
* 分别需要new对象
*
* 线程的创建方式 第二种方式由于第一种
*/
public class SellTicketTest {
public static void main(String[] args) {
//创建三个窗口
SellTicket st1 = new SellTicket() ;
SellTicket st2 = new SellTicket() ;
SellTicket st3 = new SellTicket() ;
//设置线程名称
st1.setName("窗口1");
st2.setName("窗口2");
st3.setName("窗口3");
//启动线程
st1.start() ;
st2.start() ;
st3.start() ;
}
}
package com.qf.thread_06;
/**
* @author Kuke
* @date 2021/8/4
* 线程类
*/
public class SellTicket extends Thread {
//需要保证票被共用:使用static修饰
public static int tickets = 100 ;
//重写run方法
//st1,st2,st3
@Override
public void run() {
//模拟一直有票
while(true){
//模拟网络延迟 :睡眠的过程
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(tickets>0){ //100>0
System.out.println(getName()+"正在出售第"+(tickets--)+"张票");
}
/**
* 出现同票的原因:存在: 线程的执行 原子性操作 (++,-- :简单,最直接的操作)/延迟性操作
* st1(窗口1)---->ticket 记录100 正在第100张票
* st3(窗口3)---->窗口1准备执行ticket--(100-1=99),抢占到执行权了,正在出售100张票
*
*
*/
}
}
}
多线程的实现方式2步骤
1)自定义类实现Runnable接口
2)重写Runnable接口的run方法
3)在main用户线程中
可以分配类的实例(创建类的实例)
4)创建当前类对象,然后创建Thread类对象,将当前类对象作为参数来传递
当前类---->“资源共享类”
Thread(Runnable target, String name)
5)分别启动线程即可!
public class ThreadDemo {
public static void main(String[] args) {
//可以分配类的实例(创建类的实例)
MyRunnable my = new MyRunnable() ; //资源类:被多线程共享//具体类new 具体类
//创建两个线程类对象
Thread t1 = new Thread(my,"张俊杰") ;
Thread t2 = new Thread(my,"高圆圆") ;
//分别启动线程
t1.start();
t2.start();
}
}
package com.qf.thread_01;
/**
* @author Kuke
* @date 2021/8/5
* 资源类
*/
public class MyRunnable implements Runnable {
@Override
public void run() {
//耗时的操作
for(int x = 0 ; x < 100 ; x ++){
//public static Thread currentThread()
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
第二种实现方式:
静态代理
特点:真实角色和代理角色必须实现同一个接口
真实角色:专注于自己的功能
代理角色:完成对真实角色功能的"增强"
结婚:
结婚这个这个情况
真实角色:You 你自己
代理角色:WeddingCompany 婚庆公司
public class ThreadDemo {
public static void main(String[] args) {
//接口多态
//Mary mary = new You() ;
You mary = new You() ;
mary.mary();
System.out.println("----------------------");
//静态代理:通过婚庆公司帮助自己You来完成结婚
//真实角色
You you2 = new You() ; // MyRunnable
WeddingCompany wc = new WeddingCompany(you2) ;// Thread类对象
wc.mary();
}
}
//定义一个接口的接口
interface Mary{
void mary() ;//结婚
}
//自己:真实角色
class You implements Mary{
@Override
public void mary() {
System.out.println("结婚了,很开心...");
}
}
//代理角色:婚庆公司 在你结婚之前,它可以给你布置婚礼线程, 结婚之后,开开心心吃席
class WeddingCompany implements Mary{
//将真实角色作为参数传递
private You you ;
public WeddingCompany(You you){
this.you = you ;
}
@Override
public void mary() {
System.out.println("给你布置婚礼现场...");
you.mary(); //只要专注于自己的事情!
System.out.println("婚礼线程布置完毕,吃席...");
}
}
电影院卖票—100张票,三个窗口同时出售,
第二种方式进行实现,更能体现"资源共享"
模拟真实场景:网络延迟,加入了睡眠操作
1)可能出现一张票被卖多次(同票)
2)可能出现负票(-1)
存在安全问题:
窗口1正在出售第23张
窗口3睡醒了之后,抢占到了 出售第22张… 如果出现第23张
出现负票: 线程的执行具有随机性, —加入延迟!
窗口1正在出售第1张票,延迟睡眠150秒 t3醒来之后
窗口3正在出售第0张票, –
t2也在某一刻同时醒来并执行语句---- 窗口2正在出售第-1张票
检验多线程安全问题的标准;
1)是否是多线程环境 是 不能更改,使用多线程实现
2)是否存在共享数据 是 (资源类的数据: tickets 票) 必须要有共享数据
3)是否存在多条语句对共享数据的操作 是 解决点
解决----Java提供同步机制:同步代码块 将多条对共享数据包裹起来
synchronized(锁对象){
将多条对共享数据包裹起来
}
锁对象:必须要多个线程使用的同一个锁对象,而不是分别自己的锁对象!
(生活中:火车上上厕所—> 当一个人进去之后,门一关,其他人进不来…)
public class SellTicketTest {
public static void main(String[] args) {
//创建资源类对象SellTicket
SellTicket st = new SellTicket() ;
//创建三个线程类对象
Thread t1 = new Thread(st,"窗口1") ;
Thread t2 = new Thread(st,"窗口2") ;
Thread t3 = new Thread(st,"窗口3") ;
//分别启动线程
t1.start();
t2.start();
t3.start();
}
}
public class SellTicket implements Runnable {
//成员变量;100张票
public static int tickests = 100 ;
//创建一个锁对象:
public Object obj = new Object() ;
//t1,t2,t3
@Override
public void run() {
//模拟一直票
while(true){
//t1,t2,t3
//解决方案:
//将多条语句对共享数据的操作包裹起来
//synchronized (new Object()){ //锁对象 :三个线程分别使用自己的锁
//必须为是同一个锁对象
synchronized (obj){
//模拟网络延迟
//判断
if(tickests>0){//100>0
//t1先进来,睡眠150毫秒,t1已经睡完了,执行下面的操作
//t3先进来,睡眠150毫秒,t3醒来之后
//t2最后抢占到,醒来之后
try {
Thread.sleep(100); //单位为毫秒数
} catch (InterruptedException e) {
e.printStackTrace();
}
//输出窗口信息
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickests--)+"张票");
}
}
/**
* 出现同票: 线程的原子性操作(++,--:最简单的操作)
* 原子性:记录原始数据值,然后再对数据自增或者自减
* t1执行的操作, 窗口1正在出售第 23张票 ,当准备--的时候,t3将t1里面tickets--还没执行的时候,就已经打印了
* 窗口3正在出售第23张票
*
* t2进来之后,tickets-- 动作完成了 22张
* 窗口2正在出售第22张票
*
*
* 出现负票: 线程的执行具有随机性, ---加入延迟!
*
* 窗口1正在出售第1张票,延迟睡眠150秒 t3醒来之后
* 窗口3正在出售第0张票, --
* t2也在某一刻同时醒来并执行语句---- 窗口2正在出售第-1张票
*
*/
}
}
}
synchronized(锁对象){
多条语句对共享数据的操作
}
锁对象:必须为多个线程的同一把锁;锁对象可以是任意Java的类对象
面试题:
wait()方法/notify()方法 ---- 也可以称为"同步" —>等待唤醒机制
线程等待/线程唤醒 这些方法为什么不定义在Thread类中呢,而是定义Object类中呢?
它和锁对象有关系,而锁对象:可以任何的java类对象,-----Object(顶层父类)
syncronized(锁对象){
锁对象.wait()
//锁对象.notify()
public class SellTicketTest {
public static void main(String[] args) {
//创建资源类对象SellTicket
SellTicket st = new SellTicket() ;
//创建三个线程类对象
Thread t1 = new Thread(st,"窗口1") ;
Thread t2 = new Thread(st,"窗口2") ;
Thread t3 = new Thread(st,"窗口3") ;
//分别启动线程
t1.start();
t2.start();
t3.start();
}
}
public class SellTicket implements Runnable {
//成员变量;100张票
public static int tickests = 100 ;
//创建一个锁对象:
//public Object obj = new Object() ;
//创建Demo类对
Demo d = new Demo() ;
//t1,t2,t3
@Override
public void run() {
//模拟一直票
while(true){
//t1先抢占到CPU执行权
//t1,t2,t3在抢占CPU执行权
//同步代码块
synchronized (d){ //t1进来之后,别的t2,t3线程进不来的
//t3进来,t1,t2进不来
if(tickests>0){//100>0
try {
Thread.sleep(100); //单位为毫秒数
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickests--)+"张票");
}
}//t1出来,t3出来---回到同步代码块之前,继续抢
//锁的释放,同步结束之后就会释放锁(同步锁)
}
}
}
什么是同步方法? 如果一个方法的方法体的第一句话就是同步代码块
可以将synchronized关键字提取到方法声明上,跟在权限修饰符的后面
权限修饰符 synchronized 返回值类型 方法名(式列表){ //非静态的同步方法
业务逻辑…
public class ThreadDemo {
public static void main(String[] args) {
//创建资源类对象
SellTicket st = new SellTicket() ;
//创建线程类对象 将st作为参数传递
Thread t1 = new Thread(st,"窗口1") ;
Thread t2 = new Thread(st,"窗口2") ;
Thread t3 = new Thread(st,"窗口3") ;
//启动线程
t1.start();
t2.start();
t3.start();
}
}
public class SellTicket implements Runnable {
//定义100张票
public static int tickets = 100 ;
//定义一个统计变量
int x = 0 ;
public Object obj = new Object() ;//锁对象obj
@Override
public void run() {
while(true){
if(x % 2 ==0){ //
// synchronized (obj){
// synchronized (this){ //跟下面的非静态同步方法锁对象一致
synchronized (SellTicket.class){ //跟下面的静态的同步方法锁对象一致
if(tickets>0){
//睡眠
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
}
}else{
sellTicket() ; //同步方法
//x%2!=0
/* synchronized (obj){
if(tickets>0){
//睡眠
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
}*/
}
x ++ ; //x=1
}
}
//调用了一个卖票 方法
// sellTicket() ;
//同步方法
// public synchronized void sellTicket() {//sellTicket 的锁对象是什么? 默认都是非静态的同步锁的是this:当前类对象的地址值引用
public static synchronized void sellTicket() { //静态的同步方法:锁对象,跟类相关: 当前类的字节码文件对象: 类名.class
if(tickets>0){
//睡眠
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
}
}
线程安全问题:可以通过同步方法或者是同步代码块去解决,但是执行过程中就可能出现死锁问题
死锁问题:
(使用同步机制解决线程安全) 线程和线程之间出现了互相等待的情况!
解决方案:
多个线程之间的通信:必须使用的是一个资源类对象,而不能是每一个线程在使用自己的资源类对象!
使用生成者和消费者模式思想去解决,前提条件:生成者线程和消费者线程 必须操作的同一个资源类对象!
public class DieLockDemo {
public static void main(String[] args) {
//创建资源类对象
DieLock d1 = new DieLock(true) ;
DieLock d2 = new DieLock(false) ;
//创建Thread里对象
Thread t1 = new Thread(d1) ;
Thread t2 = new Thread(d2) ;
//分别启动
t1.start();
t2.start();
}
}
public class DieLockDemo {
public static void main(String[] args) {
//创建资源类对象
DieLock d1 = new DieLock(true) ;
DieLock d2 = new DieLock(false) ;
//创建Thread里对象
Thread t1 = new Thread(d1) ;
Thread t2 = new Thread(d2) ;
//分别启动
t1.start();
t2.start();
}
}
public class DieLock implements Runnable {
private boolean flag ;//标记值
public DieLock(boolean flag){
this.flag = flag ;
}
@Override
public void run() {
//判断标记值
//t1 ---->DieLock(true)
//t2 ---->DieLock(false)
if(flag){
//t1
synchronized (MyMonitor.objA){
System.out.println("if ObjeA");//"if objA"
synchronized (MyMonitor.objB){
System.out.println("if objB");// "if objB"
}
}
}else{
//t2
synchronized (MyMonitor.objB){
System.out.println("else ObjB"); //else objB
synchronized (MyMonitor.objA){
System.out.println("else objA"); //"else objA"
}
}
}
/**
* t2线程先抢到了
* else ObjB ---->等待ObjA锁释放
* if ObjeA ---->等待ObjB锁释放
*
* t1线程先抢占到了
* if ObjeA
* else ObjB
*/
}
}
public class MyMonitor {
//提供两把锁对象
public static final Object objA = new Object() ;
public static final Object objB = new Object() ;
}
解决方案:
使用生成者和消费者思想模式—解决线程死锁问题
1)StuffBun包子类属性
包含包子的名称name
包子的大小type
2)生产者资源类 SetBun 产生包子
3)消费者资源类 GetBun 使用包子
4)ThreadDemo:main 用户线程
按照上面的方式:模拟生产者产生数据,消费者使用数据出现问题 null—null
生产资源类中和消费者资源类中所操作的包子对象不是同一个对象!
可以将包子通过生产资源类或者消费者资源类 通过构造方法传递
优化1:
加入while循环,模拟包子一直生产和一直消费!
出现问题:数据紊乱:加入同步代码块给每一个资源类中都加入解决! 将多条语句对共享数据的操作包起来!
/**包子类
*/
public class StuffBun {
//成员变量不私有化
String name ;//包子的类型(肉包子,菜包子)
String bunType ;//大包子/小包子
}
public class SetBun implements Runnable {
//声明这个包子类
private StuffBun stu ;
public SetBun(StuffBun stu){
this.stu = stu ;
}
//定义一个统计变量
int x = 0 ;
@Override
public void run() {
//产生包子
/* StuffBun stu = new StuffBun() ;
stu.name = "肉包子" ;
stu.bunType = "大类型";*/
//不断的产生数据
while(true){
synchronized (stu){
if(x % 2 == 0){//t1
stu.name = "肉包子" ;
stu.bunType = "大包子";
}else{
stu.name = "菜包子" ;
stu.bunType = "小包子" ;
}
}
x ++ ;
}
}
public class GetBun implements Runnable {
//声明包子类的变量stb
private StuffBun stb ;
public GetBun( StuffBun stb){
this.stb = stb ;
}
@Override
public void run() {
//模拟要使用数据
// StuffBun stb = new StuffBun() ;
//不断使用数据
while(true){
synchronized (stb){
System.out.println(stb.name+"---"+stb.bunType);
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//创建一个包子对象
StuffBun sbu = new StuffBun() ; //同一个对象
//创建生产资源类对象
SetBun sb = new SetBun(sbu) ;
//消费者资源类对象
GetBun gb = new GetBun(sbu) ;
//创建线程了对象
Thread t1 = new Thread(sb) ;//生产者资源类所在的生产者线程
Thread t2 = new Thread(gb) ;//消费者资源类所在的消费者线程
t1.start();
t2.start();
}
使用生成者和消费者思想模式—解决线程死锁问题
1)StuffBun包子类属性
包含包子的名称name
包子的大小type
2)生产者资源类 SetBun 产生包子
3)消费者资源类 GetBun 使用包子
4)ThreadDemo:main 用户线程
按照上面的方式:模拟生产者产生数据,消费者使用数据出现问题 null—null
生产资源类中和消费者资源类中所操作的包子对象不是同一个对象!
可以将包子通过生产资源类或者消费者资源类 通过构造方法传递
优化1:
加入while循环,模拟包子一直生产和一直消费!
出现问题:数据紊乱:加入同步代码块给每一个资源类中都加入解决! 将多条语句对共享数据的操作包起来!
synchronized(锁对象){
多条语句对共享数据的操作 ;
}
问题:
消费者资源类所在的消费者线程中,每次输出一大片的内容:
线程的执行随机性----线程抢占CPU的执行权,一点点时间片执行很多次
优化2:
想出现依次打印
肉包子—大包子
菜包子—小包子
…
wait()+notify()—>实现同步机制(并且同时信号法:将死锁问题解决!)
public class StuffBun {
//成员变量不私有化
String name ;//包子的类型(肉包子,菜包子)
String bunType ;//大包子/小包子
//定义标记:表示是否存在包子数据
boolean flag ; //默认false,没有数据
}
public class SetBun implements Runnable {
//声明这个包子类
private StuffBun stu ;
public SetBun(StuffBun stu){
this.stu = stu ;
}
//定义一个统计变量
int x = 0 ;
@Override
public void run() {
//产生包子
/* StuffBun stu = new StuffBun() ;
stu.name = "肉包子" ;
stu.bunType = "大类型";*/
//不断的产生数据
while(true){
synchronized (stu){
//如果当前生产者没有语句,需要等待生成产生数据
if(stu.flag){
//锁对象调用wait发那个发
try {
stu.wait();//释放锁对象...
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(x % 2 == 0){//t1
stu.name = "肉包子" ;
stu.bunType = "大包子";
}else{
stu.name = "菜包子" ;
stu.bunType = "小包子" ;
}
//如果现在有数据了
//改变信号
stu.flag = true ;//有数据类
//通知(唤醒)消费者线程,赶紧使用数据
stu.notify(); //唤醒对方线程
}
x ++ ;
}
}
}
public class GetBun implements Runnable {
//声明包子类的变量stb
private StuffBun stu ;
public GetBun( StuffBun stu){
this.stu = stu ;
}
@Override
public void run() {
//模拟要使用数据
// StuffBun stb = new StuffBun() ;
//不断使用数据
while(true){
synchronized (stu){
//如果当前消费资源类中存在包子数据,先等待消费使用完毕数据
if(!stu.flag){
//等待使用完毕数据
try {
stu.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(stu.name+"---"+stu.bunType);
//改变信号值
//如果包子消费完毕
stu.flag = false ;
//唤醒对方线程(生产者资源类,别等了,产生数据)
stu.notify();
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//创建一个包子对象
StuffBun sbu = new StuffBun() ; //同一个对象
//创建生产资源类对象
SetBun sb = new SetBun(sbu) ;
//消费者资源类对象
GetBun gb = new GetBun(sbu) ;
//创建线程了对象
Thread t1 = new Thread(sb) ;//生产者资源类所在的生产者线程
Thread t2 = new Thread(gb) ;//消费者资源类所在的消费者线程
t1.start();
t2.start();
}
}
Lock:
JDK5以后提供java.util.current.locks.Lock :提供比syncrhonized方法(/同步代码块)更具体的锁定操作
多个线程并发访问,抢占共享资源数据,通过lock实现多个线程对某个共享资源进行独占访问,不会安全问题!
Lock是一个接口
void lock()获取锁
void unlock() 试图释放锁
提供跟具体的子实现类:
ReentrantLock
实现电影卖票:三个窗口出售100张票 (线程的创建方式2)
public class LockDemo {
public static void main(String[] args) {
//创建共享资源类对象
SellTicket st = new SellTicket() ;
//创建三个线程类对象
Thread t1 = new Thread(st,"窗口1") ;
Thread t2 = new Thread(st,"窗口2") ;
Thread t3 = new Thread(st,"窗口3") ;
//启动线程
t1.start();
t2.start();
t3.start();
}
}
public class SellTicket implements Runnable {
//定义100张票
private static int tickets = 100 ;
//创建一个锁对象
Lock lock = new ReentrantLock() ;
@Override
public void run() {
//模拟一只有票
while(true){
//通过锁对象--->获取锁
lock.lock();
//try...catch...finaly:捕获异常
//使用try..finally
try{
//判断
if(tickets>0){
//睡眠100毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}else{
break ;
}
}finally {
//释放锁
lock.unlock();
}
}
}
}
线程组 ThreadGroup
线程组代表一组线程。 此外,线程组还可以包括其他线程组
Thread:----> public final ThreadGroup getThreadGroup() {//获取线程组
return group;
}
//给线程设置默认的线程组名称
//public Thread(ThreadGroup group, Runnable target) {
ThreadGroup:
public final String getName() {:获取默认的线程组名称
return name;
}
构造方法;
public ThreadGroup(String name) {}
线程组: 将线程可以都添加一组中,方便管理,
线程启动完毕之后,线程终止之后,不会将这个线程对象在内存中重复利用
任务队列--------一个任务称为"一个线程" , Quene
public class ThreadGroupDemo {
public static void main(String[] args) { //jvm调用main方法
// method1();
method2() ;
}
//设置一个新的线程组名称
private static void method2() {
//创建一个线程组对象--同时设置线程组名称
ThreadGroup tg = new ThreadGroup("myMain") ;
//创建两条线程对象
MyThread my = new MyThread() ;
Thread t1 = new Thread(tg,my) ;
Thread t2 = new Thread(tg,my) ;
//获取线程组对象并同时线程组名称
String name1 = t1.getThreadGroup().getName();
String name2 = t2.getThreadGroup().getName();
System.out.println(name1+"---"+name2);
}
private static void method1() {
//创建两个线程
MyThread my = new MyThread() ;
Thread t1 = new Thread(my) ;
Thread t2 = new Thread(my) ;
ThreadGroup tg1 = t1.getThreadGroup();
ThreadGroup tg2 = t2.getThreadGroup();
String name1 = tg1.getName();
String name2 = tg2.getName();
System.out.println(name1+"---"+name2); //默认线程组名称都是main
}
}
public class MyThread implements Runnable {
@Override
public void run() {
for(int x = 0 ;x < 100 ; x ++){
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
线程池
属于 "池"化技术 ------>相似 数据库连接池(dbcp,c3po,druid(为监控而生))
特点:
在内存中创建一个固定可重用的线程数,当前线程执行完毕终止了,不会被回收掉,再次
回到线程池中,等待下一次利用!
弊端: 维护成本大
ExecutorService—接口
通过 工厂类:
Exceutors
创建一个固定的可重用的线程数,返回值就线程池对象
public static ExecutorService newFixedThreadPool(int nThreads)
ExecutorService
提交队列任务(多个线程并发执行)
Future submit(Callable task)
提交值返回任务以执行,并返回代表任务待处理结果的Future。
Future<?> submit(Runnable task)
submit的返回值:异步计算的结果,如果不做计算,无须返回结果!
public class ThreadPoolDemo {
public static void main(String[] args) {
//通过工厂类创建线程池对象
ExecutorService threadPool = Executors.newFixedThreadPool(2);
//提交异步方法
//MyRunnable:打印x的值0-99之间的数据,不需要返回结果
// threadPool.submit(new MyRunnable()) ;
// threadPool.submit(new MyRunnable()) ;
// <T> Future<T> submit(Callable<T> task)
//Callable:提交异步计算---需要重写Callable的call来计算结果;如果没有结果,直接在call无须返回
threadPool.submit(new MyCallable()) ;
threadPool.submit(new MyCallable()) ;
//void shutdown()关闭线程池
threadPool.shutdown();
}
}
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int x = 0 ; x < 100 ; x ++){
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
for(int x = 0 ; x < 100 ; x++){
System.out.println(Thread.currentThread().getName()+":"+x);
}
return null;
}
}