同步方法实现线程同步
1.什么是线程同步
使用【synchronized】修饰的方法,就是同步方法
|
2.同步方法的目的
在一个线程执行该方法的时候,其他的线程只能在方法外面等待
|
3.同步方法格式
public synochronized 返回值 A(){
//可能会出现线程安全的代码,因为他访问共享了数据
}
|
4.调用方法
1.把共享的数据放到同步代码块里面
2.在RunableImpl实现类的run方法中调用
5.备注:所对象是谁(所对象是随缘给的)
- 对于飞机太同步方法,---->所对象就是this
- 对于静态的同步方法----->所对象就是:“字节码对象(RunableImpl.class)
||||||||||||||||||||||||
||||||||||||||||||||||||
||||||||||||||||||||||||
lock锁
1.为什么使用lock锁
比同步方法代码块synochronized更广泛的操作,更能体现出面向对象的特征
|
2.lock锁也称为同步锁,有枷锁和解锁的方法
*public void lock();----->加锁 - public void unlock()--->解锁
|
同步锁调用步骤
- 创建Reentrantlock对象
- 在使用共享代码的地方钱使用lock锁
- 在使用共享代码结束之后使用unlock方法,不过一般是和finally连用的哈,释放锁
||||||||||||||||||||||||
||||||||||||||||||||||||
||||||||||||||||||||||||
线程状态
1.线程状态概念
线程创建启动之后,他既不是一启动就处于运行状态,也不是一直处于执行状态,在线程生命周期有六种状态
|||| - NEW:新建状态,需要new Thread()或者new Thread的子类,调用start方法开启线程
| - 如果开启的线程没有抢到CPU使用权,那么就进入BLOCK(锁阻塞)状态,抢到了CPU使用权限就进入RUNABLE(可运行状态)。
| - 如果进入BLOCK状态的线程,拿到了CPU使用权,就可以加入RUNABLE状态,如果RUNABLE线程,失去了CPU的使用权,就会进入BLOCK状态,
| - 给RUNABLE加入sleep或者wait就会加入TIMED_WAITING(睡眠状态),如果TIMED_WAITING休眠结束或者被唤醒(notify),并且拥有1CPU使用权,那么就进入RUNABLE状态,否则BLOCK状态
| - RUNABLE的线程加入Object的wait方法就进入WAITING(无限等待状态),如果后来被唤醒(Object.notify)并且CPU空闲就进入EUNABLE,如果CPU不空闲就进入BLOCK
| - RUNABLE在run方法结束或者是调用了stop方法使得run方法停止,或者JVM停止就都会进入TERMINATED(被终止状态)
-
阻塞状态:拥有CPU执行资格,等待CPU空闲就可以执行
-
休眠状态:放弃了CPU执行资格,CPU空闲也不会执行
||||||||||||||||||||
||||||||||||||||||||
||||||||||||||||||||
TIMED_WAITING(计时等待状态)
Thread.sleep()方法使用格式:
//因为sleep是有异常的,所以需要捕获处理
try{
hread.sleep(1000); -----》表示毫秒,所以就是睡眠1秒
}catch1(Exception e){
e.printStackTrace();
}
备注 -
进入Thread-Waiting状态的易总常用的sleep方法,单独的线程可以调用,不一定非要协作关系
| -
为了让其他线程有机会执行到,所以把Thread.sleep()放到run方法内部,保证该线程能被执行到睡眠
| -
sleep与锁无关,线程睡眠的期间就会自动苏醒,但是醒了不一定马上会执行,里面的线程不会允许的【最短】因此sleep方法不能保证线程到期之后就会自动执行
|
无限等待状态
所对象.wait();是哟静止后除非遇到唤醒,notify或者notifyAll才会继续执行,但是他也会释放锁。
||||||||||||||||||||||||
||||||||||||||||||||||||
||||||||||||||||||||||||
线程之间的通信:等待唤醒机制
day25.Thread.demo04(包子,包子铺,和吃货的关系)
概念:多个线程在处理同一个资源的时候,但是处理的动作(线程的任务)不一样,线程与线程之间存在线程通信问题,此时出现线程通信问题。
|
为什么实现线程通信
多线程并发的时候CPU随机选择线程,为了让线程之间有规律执行,多线程之间就需要一些协调通信,一次来帮助我们吧达到多线程沟通操作一份数据
|
如何保证线程通信之间幼小利用资源
多个线程处理同一个资源的时候,并且任务不同,需要线程通信来帮助我们解决线程之间对同一个变量的使用和操作,就是多个线程在操作同一份数据时候,避免对共享资源造成争斗,也就是我们需要通过一定的手段使得各个线程有效的利用资源。手段就是:【等待唤醒机制】
等待机制:解决线程之间的通信问题
就是一个线程进行了规定操作之后,就进入到了等待状态(wait()),等待其他线程执行完了他们的指定代码之后,在将其唤醒(notify),有多个线程进行等待,那就使用notifyAll唤醒所有的等待线程,【wait与notify】就是县城之间的协作机制
|
等待唤醒机制的三个方法 -
wait();线程不再活动,不再进入调度,进入到WAITING设置中,以此不会浪费CPU的资源,也不会去竞争锁,需要等别的线程执行notify方法,等待中的方法重新进入调度队列中
-
notify():选取所通知对象的wait set中的一个线程进行释放。按照进入等待的线程顺序释放,最先进去,最先释放
-
notifyAll():释放所有的等待线程,所有的
|
备注:
哪怕只通知了一个等待线程,被通知的也不能立刻执行,因为当时中断的时候在同步块内,此刻已经不持有锁了,所以不会执行,只有再次获取锁的时候,获得后才能继续执行。如果拿不到就进入 BLOCK(锁阻塞状态)
|
调用wait和notify注意细节: -
wait和notify必须由同一个所对象调用,否则就不是同一个所对象就不能把WAITING状态的线程唤醒。
-
wait和nofity方法是属于Object方法的,因为所对可以是任意对象,Object是根父类
-
wait和notify方法必须要在同步代码块或者同步方法中使用,因为必须通过所对象调用两个方法实现线程通信
|||||||||||||||||||||||
|||||||||||||||||||||||
|||||||||||||||||||||||
线程池
1.概念
就是可以容纳多个线程的容器,其中的线程可以反复调用,省去了频繁的创建线程对象的操作,无需反复创建线程而消耗过多的系统资源,由于线程池中有很多操作都是针对优化系统资源的
|
原理
- 本质就是一个容器,线程池底部就是通过集合来实现的,集合里面的数据就是线程。
- 当程序第一次启动的时候,需要一次性创建多个线程保存到一个集合中。当我们使用的时候直接用Thread t=list.remove(index);线程并没有消失,只是不在集合里面了,不用get原因,避免西完成重复调用。
- 调用完成之后,请还回去,list.add(t)。但是在JDK1.5之后就不要我们人为操作了
- 线程池内部线程产生线程对象,任务接收到线程对象就可以执行,否则就只能等待其他任务完成释放线程对象之后才能继续接收释放出来的线程对象
|
3.线程池优势 - 降低资源消耗,减少了线程池的创建和销毁,每个工作线程都可以被重复利用,课执行多个任务
- 提高了响应速度,当任务到达时候,任务不需要等到县城创建,直接调用线程对象执行。
- 提高了线程池可管理性,可以根据系统承受能力,调整线程池工作线程数目,防止服务器崩溃
|
线程池的使用
1.创建线程池方法
public static ExecutorService newFixed(int nThread);返回的就是线程池对象,内写最大的容量。获得一个ExecutorService对象,
|
2.线程池对象的方法
public Future<?> submit(Runable task):获取线程中某一个线程对象并执行
|
使用线程步骤 - 创建线程池对象
- 创建Runable接口子类对象(task)
- 提交Runable接口子类对象
- 关闭线程池,一般不做(项目完了才做)