线程和多线程
程序:是一段静态的代码,是应用软件执行的蓝本
进程:是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完整过程,这个过程也是进程本身从产生、发展至消亡的过程
线程:是比进程更小的执行单位。进程在其执行过程中,可以产生多个线程,形成多条执行线索,每条线索,即每个线程也有它自身的产生、存在和消亡的过程,也是一个动态的概念
进程: 线程:
一个应用程序一个进程 一个进程可以有多个线程
独立功能的程序 执行程序的最小单元
通过多个线程占据系统资源 占用CPU的基本单位
进程之间数据状态完全独立线程间共享一块内存空间
主线程:(每个Java程序都有一个默认的主线程)
当JVM加载代码发现main方法之后,就会立即启动一个线程,这个线程称为主线程
是产生其他子线程的线程,但不一定是最后完成执行的线程
单线程:如果main方法中没有创建其他的线程,那么当main方法执行完最后一个语句,JVM就会结束Java应用程序
多线程:如果main方法中又创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,JVM要等程序中所有线程都结束之后才结束程序
多线程的优势:
减轻编写交互频繁、涉及面多的程序的困难
程序的吞吐量会得到改善
由多个处理器的系统,可以并发运行不同的线程
“同时”执行是人的感觉,在线程之间实际上轮换执行
线程生命周期(五个状态):新建、就绪、运行、阻塞、死亡
新建状态:线程对象已经创建,还没有在其上调用start()方法
就绪状态:当线程调用start方法,但调度程序还没有把它选定为运行线程时线程
运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。(是线程进入运行状态的唯一方式)
阻塞(等待/睡眠)状态:线程仍旧是活的,但是当前没有条件运行。当某件事件出现,他可能返回到可运行状态
死亡状态:当线程的run()方法完成时就认为它死去。线程一旦死亡,就不能复生。 一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常
Java中两种创建线程的方式:
1.继承Thread类
重写run() 方法
new一个线程对象
调用对象的 start() 启动线程
优点:编写简单,如果需要访问当前线程直接使用this即可获得当前线程.
缺点:因为线程类已经继承了Thread类,不能再继承其他的父类
2.实现Runnable接口
实现run() 方法
创建一个Runnable类的对象r,new MyRunnable()
创建Thread类对象并将Runnable对象作为参数,new Thread(r)
调用Thread对象的start()启动线程
优点:线程类只实现了Runable接口,还可以继承其他的类.
缺点:编程稍微复杂,需要访问当前线程,必须使用Thread.currentThread()方法
线程创建的问题:
线程的名字:JVM给的名字或者我们自定义的名字,通过setName方法设置
获取当前线程对象:Thread.currentThread()
在一个程序里多个线程只能保证其开始时间,而无法保证其结束时间,执行顺序也无法确定
一个线程的run方法执行结束后,该线程结束
一个线程只能被启动一次,一次只能运行一个线程
JVM线程调度程序决定实际运行哪个处于可运行状态的线程。采用队列形式
线程中的常用方法:
start():启动线程,让线程从新建状态进入就绪队列排队
run():线程对象被调度之后所执行的操作
sleep():暂停线程的执行,让当前线程休眠若干毫秒
currentThread():返回对当前正在执行的线程对象的引用
isAlive():测试线程的状态,新建、死亡状态的线程返回false
interrupt():“吵醒”休眠的线程,唤醒“自己”
yield():暂停正在执行的线程,让同等优先级的线程运行
join():当前线程等待调用该方法的线程结束后,再排队等待CPU资源
stop():终止线程
阻止线程执行的方法:
线程睡眠:(当线程睡眠时,它暂停执行,当睡眠时间到期,则返回到可运行状态)
Thread.sleep()
使用场景:线程执行太快
需要强制设定为下一轮执行
线程睡眠是帮助其他线程获得运行机会的最好方法
线程睡眠到期自动苏醒,并返回到可运行状态(不是运行状态)
sleep()中指定的时间是线程不会运行的最短时间(sleep()方法不能保证该线程睡眠到期后就开始执行)
sleep()是静态方法,只能控制当前正在运行的线程
线程的优先级
设置线程优先级:
1.线程默认优先级是创建它的执行线程的优先级
2.通过Thread实例调用setPriority()方法设置线程优先级
Thread.MIN_PRIORITY (1)
Thread.NORM_PRIORITY (5)
Thread.MAX_PRIORITY (10)
通过Thread示例调用getPriority()方法得到线程优先级
线程让步(yield方法 暂停当前正在执行的线程对象,并执行同等优先级的其他线程)
Thread.yieId();
yield()将导致线程从运行状态转到可运行状态,有可能没有效果无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中
线程离开运行状态的方法:
1.调用Thread.sleep():使当前线程睡眠至少多少毫秒(尽管它可能在指定的时间之前被中断)
2.调用Thread.yield():不能保障太多事情,尽管通常它会让当前运行线程回到可运行性状态,使得有相同优先级的线程有机会执行
3.调用join()方法:保证当前线程停止执行,直到调用join方法的线程完成为止。然而,如果调用join的线程没有存活,则当前线程不需要停止
4.线程的run()方法完成
多线程问题——资源协调
两个线程A和B在同时使用Stack的同一个实例对象,A正在往堆栈里push一个数据,B则要从堆栈中pop一个数据
资源同步——对象互斥锁
关键字synchronized与对象互斥锁联合起来使用保证对象在任意时刻只能由一个线程访问
synchronized可以修饰方法,表示这个方法在任意时刻只能由一个线程访问
synchronized可以修饰类,则表明该类的所有对象共用一把锁
多线程同步模型(生产者——消费者示例)
多线程问题——死锁(两个或两个以上的线程在执行过程中,因争夺资源而造成了互相等待)
产生死锁的必要条件
互斥条件:指线程对所分配到的资源进行排它性使用
请求和保持条件:指线程已经保持至少一个资源,但又提出了新的资源请求
不可剥夺条件:进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放
环路等待条件:指在发生死锁时,必然存在一个线程—资源的环形链
出现死锁的情况
相互排斥:一个线程永远占用某一共享资源
循环等待:线程A在等待线程B,线程B在等待线程C,线程C在等待线程A
部分分配:线程A得到了资源1,线程B得到了资源2,两个线程都不能得到全部的资源
缺少优先权:一个线程访问了某资源,但一直不释放该资源,即使该线程处于阻塞状态