一、多线程
1.线程与进程
进程(是软件):时机一个内存中运行的应用程序,每个进程都有一个独立的内存空间
线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程,线程实际上是在进程基础上的进一步划分,一个进程启动后,里面的若干执行路径又可以划分为若干个线程
2.线程调度
分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CUP的时间
抢占式调度:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机),java使用为抢占式调度
3.同步(线程安全)与异步(线程不安全)
同步:排队执行,效率低但是安全
异步:同时执行,效率高但是数据不安全
4.并发与并行
并发:指两个或多个事件在同一个时间段内发生
并行:指两个或多个事件在同一时刻发生(同时发生)
5.多线程技术
①.//创建一个类继承Thread 并实现抽象方法run() run里写入要同时运行的程序
public void MyThread extends Thread(){
run(){
//运行程序
}
}
//在main方法里要调用对象的start 方法来执行run方法里的程序
MyThread m = new MyThread();
m.start();
main方法是主线程 而run方法是分支线程 程序是同时运行但先后顺序不是固定的是 随机的
每个线程都拥有自己的栈空间,公用一分堆内存。由一个线程所调用的方法 那么这个方法也会调用在这个线程里面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6zEy53qr-1610471007401)(C:\Users\lenovo\Desktop\笔记\多线程图解.png)]
②.实现接口Runnable 实现抽象方法run()
public void class MyRunnable implents Runnable(){
run(){
//执行的任务
}
}
//main方法中
//创建一个任务对象
MyRunnable r = new MyRunnable();
//创建一个线程,并为其分配一个任务
Thread t = new Thread(r);
//执行
t.start();
③. 实现Runnable 与继承Thread相比有如下优势:
通过创建任务,然后给线程分配的方式来实现多线程,更适合多个线程同时执行相同任务的情况
可以避免单继承所带来的的局限性
java中只有单继承 但是有多实现
任务与线程本身是分离的,提高了程序的健壮性
后续学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程
二、Thread类
1.构造方法
一参任务 二参任务名 通过getname可以获取任务名
2.方法
停止的方法 定义一个变量让线程时刻查看这个变量 然后return
sleep方法:休眠 传参1 毫秒 停止运行多少毫秒 参2 纳秒 多少毫秒纳秒
设置和获取线程名称: 在run方法中 调用Thread.currentThread().getName() 或setName() 默认名称是Thread-0
Thread.currentThread() 获取当前正在执行的线程 返回值类型是Thread
三、线程的休眠
Thread.sleep(1000); 相当于1秒钟运行一次 要进行trycatch处理
四、线程阻塞
所有消耗时间的操作和比较消耗时间的操作 比如接受用户输入 或读文件
五、线程中断
一个线程是一个独立的执行路径,他是否应该结束,应该由其自身决定
Thread t = new Thread();
方法 t.interrupt();//给线程添加中断标记 然后在catch块中添加return
六、守护线程
线程分为:守护线程和用户线程
用户线程:当一个进程不包含任何的存活的用户线程时,进程结束 手动设置的线程都是用户线程
守护线程:守护用户线程的 ,当最后一个用户线程结束时,所有守护线程自动死亡
设置守护线程一定要在start之前设置
线程对象.setDaemon(true);
七、线程安全问题
多个线程同时处理一个变量可能出现都进入while循环 循环条件是i>0 每循环一次i-- 但是最后结果打印i是小于0的
八、线程同步 (加锁机制)
方案1 同步代码块 隐式锁
格式:synchronized(锁对象){} java中任何东西都能是锁对象
要让所有线程都看同一把锁才能排队 不能每人一把 把if语句锁起来
方案2 同步方法 隐式锁
把要排队的部分编辑成方法 然后在方法中添加synchronized
锁机制是this 如果是静态修饰 就是类名.class
public synchronized void sale(){
}
这个方法只能是同一个对象
如果同步方法和同步代码块同时使用并且都是this对象 那么同步代码块被调用 同步方法别人也不能用
方案3 显示锁
Lock 子类 ReentrantLock
//创建一把锁
Lock l = new ReentrantLock();
//锁
l.lock();
//解锁
l.unlock();
九、公平锁和非公平锁
意思就是:是不是解锁线程一起抢 谁抢到谁用
上面三种锁机制都是非公平锁
显示锁可以传参 Lock l = new ReentrantLock(true); 参数默认是false true就变成了公平锁
谁先来谁先得到这个锁
十、线程死锁
比如两个人在两个试衣间 a在1试衣间 b在2试衣间 a有一个方法让他去2试衣间 但是2试衣间有锁b在里面那么他要等b出来 而b有一个方法让他去1试衣间 但1试衣间有锁a在里面 他要等a出来 因此互等产生死锁
解决方法:在任何有可能导致锁产生的方法里,不要在调用另外一个方法让另外一个锁产生
十一、多线程通信问题
举例: 厨师 服务员 一个盘子 盘子空了厨师醒 服务员睡 盘子满了 服务员醒厨师睡
在盘子里定义一个boolean类型flag 如果flag等于true 厨师开始做饭 然后做完之后flag等于false 同时this.notifyAll(); 唤醒服务员 this.wait(); 自己睡眠 服务员那边是flag是false时进入 端完菜之后flag等于true this.notifyAll(); 唤醒厨师 this.wait(); 自己睡眠
服务员和厨师都要上锁 用同步方法
十二、线程的六种状态
new :线程刚创建出来 是new状态 还没启动
Runnable:正在执行的线程
Blocked:被阻塞等待的线程状态 排队
Waiting:无线等待另一个线程执行特定操作的状态 休眠
TimeWaiting:指定等待时间
Trminated :程序结束 线程死亡了 被终止了
无论哪种状态最终都会走到Trminated状态
十三、带返回值的线程Callable
Callable接口
实现方法 call() 运行线程方法
task.get() 是让主线程等子线程结束了在执行 收到返回值
task.isDone();判断这个线程是否执行完毕了
task.cancel(); 取消这个线程 有返回值true或false 一般false是这个线程已经结束了
十四、线程池 装线程的容器
创建线程 创建任务 执行任务 关闭线程
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁的创建线程就会大大的降低系统的效率,因为创建和销毁线程需要时间,线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程的操作
1.缓存线程池
流程:判断线程池是否存在空闲线程 存在测使用 不存在,则创建线程,并入线程池,然后使用
2.定长线程池
流程:判断线程池是否存在空闲线程 存在使用 不存在且线程池未满,则创建线程并入线程池,然后使用
不存在且线程池已满,则等待线程池存在空闲线程
3.单线程线程池
流程: 判断线程池的线程是否空闲 空闲则使用 不空闲则等待线程空闲再使用
4.周期性任务定长线程池
流程: 判断线程池是否存在空闲线程 存在使用 不存在且线程池未满,则创建线程并入线程池,然后使用
不存在且线程池已满,则等待线程池存在空闲线程
可以不是立即执行 可以定时执行
十五、缓存线程池
//创建线程池
ExecutorService s = Executors.newCachedThreadPool();
//向线程池中加入新的任务并执行
s.execute(new Runnable(){
public void run(){
syso(Thread.currentThread().getName()+"汗滴禾下土");
}
});
十六、定长线程池
ExecutorService s = Executors.newFixedThreadPool(2);// 给一个线程长度
s.execute(线程);
十七、单线程线程池
ExecutorService s = Executors.newSingleThreadExecutor();
s.execute(线程);
十八、周期定长线程池
ScheduledExecutorService s = Executors.newScheduledThreadPool(2);//传 线程数
//定时执行一次
//参数1 定时执行任务 参数二 时长数字 参数三 时长数字的时间单位 , TimeUnit的常量指定
s.schedule(任务线程,5,TimeUnit.SECONDS); // 5秒
//周期执行
//参数1 任务 参数2 延迟时长数字(第一次执行在什么时间以后) 参数3 周期时长数字(每隔多久执行一次) 参数4 时长数字单位
s.scheduleAtFixedRate(任务,5,1,TimeUnit.SECONDS); //五秒钟以后执行 每隔一秒执行一次
十九、Lambda表达式 函数式编程思想
实现前提就是这个接口只有一个方法
就是保留方法参数部分以及方法体
//冗余的Runnable代码
Thread t = new Thread(new Runnable(){
public void run(){
syso(Thread.currentThread().getName()+"汗滴禾下土");
}
});
//函数式编程思想
Thread t = new Thread( () -> syso(Thread.currentThread().getName()+"汗滴禾下土"));
//也可以
Thread t = new Thread( () -> {
syso(Thread.currentThread().getName()+"汗滴禾下土");
});