一、线程
1、程序、进程、线程定义
程序:一段保存在物理介质中的代码
进程:在操作系统上运行起来的一段程序
线程:程序运行时的一条独立的执行线索
多线程:就是让程序中拥有多条独立的执行线索,从而同一时间可以完成多项任务,服务于多个用户。在某些场景下,使用多线程可以提高效率,但是使用多线程的目的是为了可以同时做多件事,从而服务于多个用户。
2、线程的七大状态
新生 就绪 运行 消亡 阻塞 锁池 等待池
3、实现线程的三种方式
1、extends Thread 2、implements Runnable 3、implements Callable<T>
import java.util.concurrent.*;
public class TestThreadPool{public static void main(String[] args)throws Exception{// Executors.newSingleThreadExecutor();//单一实例的// Executors.newCachedThreadPool();//缓存机制的ExecutorService es = Executors.newFixedThreadPool(2);//固定大小 修复后可重用的ThreadOne t1 = new ThreadOne();es.submit(t1);ThreadTwo t2 = new ThreadTwo();es.submit(t2);ThreadThree t3 = new ThreadThree();Future<String> f = es.submit(t3);for(int i = 0;i<10;i++){System.out.println("做任何与t3返回值无关的其它业务");}es.shutdownNow();}
}/*Callable接口的出现 修正了原本Runnable接口的两大不足1.run()被定义为void方法 执行结束无法返回数据2.run()没有任何throws声明 逼迫程序员必须try catch
*/
class ThreadThree implements Callable<String>{@Overridepublic String call()throws Exception{for(int i = 0;i<666;i++){System.out.println("我是创建线程的第3种方式");}return "ETOAK VIVA";}
}
class ThreadTwo implements Runnable{@Overridepublic void run(){for(int i = 0;i<666;i++){System.out.println("我是创建线程的第2种方式");}}
}
class ThreadOne extends Thread{@Overridepublic void run(){for(int i = 0;i<666;i++){System.out.println("我是创建线程的第1种方式");}}
}
4、控制线程的四种方法
1、setPriority(int) 设置优先级
2、static sleep(long) : 使该线程休眠long毫秒
java.util.concurrent.TimeUnit.SECONDS.sleep(1); 使该线程休眠long秒
3、static yield(): 让当前线程放弃时间片直接返回就绪
4、join() : 当前线程邀请调用方法的线程优先执行
线程类所有静态方法 不要关注谁调用方法,而要关注调用出现在谁的线程体当中。
线程类所有主动进入阻塞状态的方法,都有throws声明,所以必须进行异常处理
5、设置一个守护线程
1、守护线程应当无限循环 以防止其过早消亡。
2、守护线程的设置必须早于启动,否则触发IllegalThreadStateException
3、守护线程应当具有较低的优先级,尽量不要让守护线程和核心线程抢夺时间片
public class TestSetDaemon{public static void main(String[] args){GYJJ gy = new GYJJ();gy.setDaemon(true);gy.start();// * : 守护线程应当具有较低的优先级别 以防止其与核心业务争抢时间片gy.setPriority(1);for(int i = 0;i<666;i++){System.out.println("西天取经上大路 一走就是几万里");}}
}
class GYJJ extends Thread{@Overridepublic void run(){//*: 守护线程通常都是无限循环 以防止其过早消亡while(true){System.out.println("你这泼猴儿...");}}
}
6、关于JUC包
java.util.concurrent => 并发包
1> 高并发情况下更好的集合
ConcurrentHashMap CopyOnWriteArrayList CopyOnWriteArraySet
2> 多线程高并发的场景下 某些常用工具:倒计时门闩
CountDownLatch
import java.util.*;
import java.util.concurrent.*;
/*1.如何实现线程 如何给线程布置任务 如何启动线程2.如何给线程设置名字 如何得到线程的名字 setName() + getName()3.单例模式 - 醉汉式(预先加载)4.run()方法调用的其它方法当中 如何得到当前线程是谁5.多线程高并发的场景下 应该选择哪种键值对(Map)集合 ConcurrentHashMap6.Map集合的基础操作 put() containsKey() get() keySet() forEach()7.分析处理数据的线程 如何邀请生产数据的线程优先执行 join()8.连环try9.CountDownLatch => 倒计时门闩 since JDK5.0构造方法: 传参(int)指定门上装多少个门闩核心方法:countDown() : 打开一个门闩await() : 主动制造阻塞 等待开门
*/
public class TestCurrentThread2{public static void main(String[] args){Student s1 = new Student("小翔");Student s2 = new Student("小俐");Student s3 = new Student("小黑");s1.start();s2.start();s3.start();try{X.latch.await();//主动阻塞 等待开门}catch(Exception e){e.printStackTrace();}//查账 收包子System.out.println("========= 开始查账 ==========");Teacher tea = Teacher.getOnly();{Map<Integer,String> temp = new TreeMap<>((a,b) -> a.equals(b) ? 1 : b.compareTo(a));tea.map.forEach((k,v) -> temp.put(v,k));temp.forEach((count,name) -> System.out.println(name + " : " + count));}System.out.println("========= 查账结束 ==========");}
}
class X{static CountDownLatch latch = new CountDownLatch(3);
}
class Student extends Thread{public Student(String name){setName(name);}@Overridepublic void run(){System.out.println("我叫"+getName()+"学习遇到了问题 准备问老师");Teacher tea = Teacher.getOnly();int x = (int)(Math.random()*3)+3;//3-5for(int i = 0;i<x;i++){tea.hdwt();}X.latch.countDown();//打开一个门闩}
}
class Teacher{Map<String,Integer> map = new ConcurrentHashMap<>();private Teacher(){}private static Teacher only = new Teacher();public static Teacher getOnly(){return only;}public void hdwt(){Thread x = Thread.currentThread();String name = x.getName();System.out.println("认真的回答"+name+"同学的问题");//记账 记录name同学又多欠他一个包子if(map.containsKey(name)){Integer v = map.get(name);map.put(name,v+1);}else{map.put(name,1);}}
}
3> OO思想实现的锁!
java.util.concurrent.locks.ReentrantLock
import java.util.concurrent.locks.*;
public class TestSwitchThreadWithLock{public static void main(String[] args){RightThread rt = new RightThread();//LeftThread lt = new LeftThread(rt);lt.start();//}
}
class X{static Lock lock = new ReentrantLock();static Condition cdt = lock.newCondition();//新建阻塞条件
}
class LeftThread extends Thread{RightThread rt;public LeftThread(RightThread rt){this.rt = rt;}@Overridepublic void run(){X.lock.lock();rt.start();//for(int i = 0;i<666;i++){System.out.println("A玩家的一组操作");//1st.try{X.cdt.await();}catch(Exception e){e.printStackTrace();}//2nd.X.cdt.signal();//6th.}X.lock.unlock();}
}
class RightThread extends Thread{@Overridepublic void run(){X.lock.lock();for(int i = 0;i<666;i++){System.out.println(" B玩家的一组操作");//3rd.X.cdt.signal();//4th.try{X.cdt.await();}catch(Exception e){e.printStackTrace();}//5th.}X.lock.unlock();}
}
4> 线程池实现
ExecutorService Executors Future
import java.util.concurrent.*;
public class TestThreadPool{public static void main(String[] args)throws Exception{// Executors.newSingleThreadExecutor();//单一实例的// Executors.newCachedThreadPool();//缓存机制的ExecutorService es = Executors.newFixedThreadPool(2);//固定大小 修复后可重用的ThreadOne t1 = new ThreadOne();es.submit(t1);ThreadTwo t2 = new ThreadTwo();es.submit(t2);ThreadThree t3 = new ThreadThree();Future<String> f = es.submit(t3);for(int i = 0;i<10;i++){System.out.println("做任何与t3返回值无关的其它业务");}es.shutdownNow();System.out.println("===========================");}
}/*Callable接口的出现 修正了原本Runnable接口的两大不足1.run()被定义为void方法 执行结束无法返回数据2.run()没有任何throws声明 逼迫程序员必须try catch
*/
class ThreadThree implements Callable<String>{@Overridepublic String call()throws Exception{for(int i = 0;i<666;i++){System.out.println("我是创建线程的第3种方式");}return "ETOAK VIVA";}
}
class ThreadTwo implements Runnable{@Overridepublic void run(){for(int i = 0;i<666;i++){System.out.println("我是创建线程的第2种方式");}}
}
class ThreadOne extends Thread{@Overridepublic void run(){for(int i = 0;i<666;i++){System.out.println("我是创建线程的第1种方式");}}
}
7、懒汉式单例模式
public class TestSingleton{public static void main(String[] args){}
}
class Sun{private Sun(){}private static Sun only;//nullpublic static synchronized Sun getOnly(){if(only == null)only = new Sun();return only;}
}
8、并发错误
(1)定义
多个线程共享操作同一份数据的时候,线程体当中连续的多行操作未必能够连续执行,很可能操作只完成了一部分,时间片突然耗尽,而另一个线程抢到了时间片,直接访问了操作不完整的数据。
(2)如何解决并发错误
语法内置的加锁: synchronized修饰符,能够修饰代码块、修饰整个方法,隔代丢失,只对当前类型有效。
修饰代码块:synchronized(临界资源){需要连续执行的操作1;需要连续执行的操作2;....;}修饰方法:public synchronized void add(Object obj){需要连续执行的操作1;需要连续执行的操作2;...;}
面向对象的加锁: ReentrantLock可重入锁
lock() unlock()
加锁 释放锁
它可以指定公平锁或非公平锁 new ReentrantLock(true);
9、死锁
多个线程相互持有对方想要申请资源不释放的情况下,又去申请对方已经持有的资源,从而双双进入对方已经持有的资源的锁池当中,产生永久的阻塞
如何解决死锁
使用对象的等待池和操作等待池的wait() notify() notifyAll()
public class TestDeadLock{public static void main(String[] args){QCRoad qcl = new QCRoad();QCRoad.Benz s900 = qcl.new Benz();QCRoad.Bmw x9 = qcl.new Bmw();s900.start();x9.start();}
}
class QCRoad{Object east = new Object(); //路东Object west = new Object(); //路西class Benz extends Thread{@Overridepublic void run(){System.out.println("孟总驾驶奔驰驶出家门去上课");synchronized(east){System.out.println("孟总和他的奔驰已经占领了泉城路东侧");try{java.util.concurrent.TimeUnit.SECONDS.sleep(1);}catch(Exception e){e.printStackTrace();}try{east.wait();}catch(Exception e){e.printStackTrace();}synchronized(west){System.out.println("孟总和他的奔驰又占领了泉城路西侧");}}System.out.println("孟总顺利的通过了泉城路");}}class Bmw extends Thread{@Overridepublic void run(){System.out.println("高总驾驶宝马驶出家门去上课");synchronized(west){System.out.println("高总和他的宝马已经占领了泉城路西侧");try{java.util.concurrent.TimeUnit.SECONDS.sleep(1);}catch(Exception e){e.printStackTrace();}synchronized(east){System.out.println("高总和他的宝马又占领了泉城路东侧");east.notify();}}System.out.println("高总顺利的通过了泉城路");}}
}
10、线程池
线程池是一种标准的资源池模式
资源池:是指在用户出现之前 提前预留活跃资源,从而在用户出现的第一时间,直接满足用户对资源的需求,并且将资源的创建和销毁都委托给资源池完成,从而优化用户体验
假如一个线程的完整执行时间为T
T = t1 + t2 + t3
t1: 在操作系统当中映射一个线程所消耗的时间
t2: 线程核心逻辑执行的时间 run()
t3: 在操作系统当中销毁一个线程所消耗的时间
如果run()当中代码非常简短 则t2所占T的比例就会很小,我们会感觉喧宾夺主 主次不分
import java.util.concurrent.*;
public class TestThreadPool{public static void main(String[] args)throws Exception{// Executors.newSingleThreadExecutor();//单一实例的// Executors.newCachedThreadPool();//缓存机制的ExecutorService es = Executors.newFixedThreadPool(2);//固定大小 修复后可重用的ThreadOne t1 = new ThreadOne();es.submit(t1);ThreadTwo t2 = new ThreadTwo();es.submit(t2);ThreadThree t3 = new ThreadThree();Future<String> f = es.submit(t3);for(int i = 0;i<10;i++){System.out.println("做任何与t3返回值无关的其它业务");}es.shutdownNow();System.out.println("===========================");}
}/*Callable接口的出现 修正了原本Runnable接口的两大不足1.run()被定义为void方法 执行结束无法返回数据2.run()没有任何throws声明 逼迫程序员必须try catch
*/
class ThreadThree implements Callable<String>{@Overridepublic String call()throws Exception{for(int i = 0;i<666;i++){System.out.println("我是创建线程的第3种方式");}return "ETOAK VIVA";}
}
class ThreadTwo implements Runnable{@Overridepublic void run(){for(int i = 0;i<666;i++){System.out.println("我是创建线程的第2种方式");}}
}
class ThreadOne extends Thread{@Overridepublic void run(){for(int i = 0;i<666;i++){System.out.println("我是创建线程的第1种方式");}}
}
如果自己创建一个线程池执行器,需要多少个参数,各自代表什么含义
new ThreadPoolExecutor(1,2,3,4,5);
1: 线程池当中 核心线程的数量
2: 线程池当中 最大线程数量
3: 保持活着的时间 KeepAliveTime
4: 时间单位 TimeUnit
5: 一个Queue (是一种集合)
11、shutdown() 和 shutdownNow()的区别
它们都能够禁止新任务再次提交,它们都不能够停止正在执行中的线程任务
区别在于已经提交但是还没开始执行的线程任务
shutdownNow() : 会被直接退回 无法执行
shutdown() : 正常执行结束