一、线程
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>{
@Override
public String call()throws Exception{
for(int i = 0;i<666;i++){
System.out.println("我是创建线程的第3种方式");
}
return "ETOAK VIVA";
}
}
class ThreadTwo implements Runnable{
@Override
public void run(){
for(int i = 0;i<666;i++){
System.out.println("我是创建线程的第2种方式");
}
}
}
class ThreadOne extends Thread{
@Override
public 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{
@Override
public 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)集合 ConcurrentHashMap
6.Map集合的基础操作 put() containsKey() get() keySet() forEach()
7.分析处理数据的线程 如何邀请生产数据的线程优先执行 join()
8.连环try
9.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);
}
@Override
public void run(){
System.out.println("我叫"+getName()+"学习遇到了问题 准备问老师");
Teacher tea = Teacher.getOnly();
int x = (int)(Math.random()*3)+3;//3-5
for(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;
}
@Override
public 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{
@Override
public 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>{
@Override
public String call()throws Exception{
for(int i = 0;i<666;i++){
System.out.println("我是创建线程的第3种方式");
}
return "ETOAK VIVA";
}
}
class ThreadTwo implements Runnable{
@Override
public void run(){
for(int i = 0;i<666;i++){
System.out.println("我是创建线程的第2种方式");
}
}
}
class ThreadOne extends Thread{
@Override
public 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;//null
public 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{
@Override
public 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{
@Override
public 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>{
@Override
public String call()throws Exception{
for(int i = 0;i<666;i++){
System.out.println("我是创建线程的第3种方式");
}
return "ETOAK VIVA";
}
}
class ThreadTwo implements Runnable{
@Override
public void run(){
for(int i = 0;i<666;i++){
System.out.println("我是创建线程的第2种方式");
}
}
}
class ThreadOne extends Thread{
@Override
public 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() : 正常执行结束