1.线程概述
线程是程序运行的基本执行单元。在操作系统中可以有多个进程,包括系统进程(操作系统内部建立的进程)和用户进程(用户程序建立的进程);一个进程中可以有一个或者多个线程。进程和进程之间是不共享内存,也就是说系统中的进程是在各自独立的内存空间中运行的。而一个进程中的线是可以共享系统分配给这个进程的内存空间。
线程不仅可以共享进程的内存,还拥有一个属于自己的内存空间,这段内存空间也叫线程栈,是建立线程时系统分配的,主要用来保存线程内部所使用的数据,如线程执行函数中所定义的变量。
进程分成多个线程后,这些线程可以在操作系统的管理下并发执行,从而提高程序的运行效率。从宏观上看是多个线程的同时执行,但这只是系统的障眼法。因为一块cpu同时只能执行一条指令,所以在拥有一块cpu的计算机上不可能同时执行两个任务。此时操作系统为了提高运行效率,在一个线程空闲时会撤下这个线程,并让其他线程来执行,这叫做线程调度。之所以表面上看来多个线程同时进行是因为不同线程之间切换时间非常短,而且一般情况切换非常频繁,可能只需要1毫秒。
2.线程好处
1.充分利用cpu资源
2.简化编程模型
3.简化异步事件的处理
4.使GUI更有效率
5.节约成本
3.线程模型
java中建立线程方法有两种
1.继承Thread类,重写Thread类的run方法,实例化对象调用start方法运行run方法。
2.新建类实现Runnable接口,重写接口抽象方法run。实例化新建类对象,并在建立Thread对象时将新建类对象作为参数传入Thread类的构造方法,通过Thread对象调用start方法。
由于java支持单继承,推荐实现接口的方法建立线程。
4.执行方法
(1)sleep()
该方法是Thread类的静态方法,使线程处于睡眠等待的状态,让出cpu的执行权。
(2)interrupt()
需要捕获InterruptException的异常,当调用线程的该方法时,停止线程的执行,但是会先执行完InterruptException的异常处理之后,再结束线程。
(3)stop()
停止线程,该方法已经废弃,一旦调用会立即结束线程。(不推荐,结束后线程无法重启)
线程结束方法对比:1.stop方法比interrupt方法更粗暴,直接结束线程;
2.结束线程最好的方法是结束run()方法的执行。当run()执行结束,线程也就结束了。
(4)join()
合并两个线程的执行。当在线程A中执行调用线程B的join方法时,线程B合并到A中执行,B结束后再接着执行线程A。
(5)yield()
当调用线程的该方法时,该线程立刻释放CPU的占用,重新进入就绪等待的状态。
(6)isAlive()
判断当前线程是否还存活:Thread.currentThread().isAlive();
5.线程同步
线程同步:线程程序运行有先后顺序,前面的运行完再运行后面的,保证线程能了解其他线程任务处理结束后的处理结果而采取的保护措施。(同,在此意思是协同,互相配合的意思)
直接理解为资源独占,也就是同一时期的某个资源只能被一个线程独占。
当两个或两个以上的线程共享某些资源或需要相互配合来完成某些工作时,就必须通过线程同步来协调各个线程运行的次序。
线程异步:一个线程去执行,它的下一个线程不必等待它执行完就开始执行。
(1)互斥锁
资源独占可以用synchronized关键字进行修饰。
①修饰方法:public synchronized void printDataA(){ }
当synchronized修饰方法时,该线程拥有该对象的锁,则其他线程会阻塞无法访问该方法,知道该线程释放锁。
②修饰代码块:public void printDataB() {synchronized (this) {方法 }}
(2)死锁
死锁是指两个线程或多个线程之间相互等待需要的资源对象的现象,由于线程之间的相互等待,导致线程永远处于阻塞状态无法继续执行。经典的问题如哲学家吃饭。
(3)消费者生产者问题(生产者/消费者模型,是典型的容易发生死锁现象的问题)