20175319 2018-2019-2 《Java程序设计》第10周学习总结
教材学习内容总结
本周学习《Java程序设计》第十二章java多线程机制:
- 进程与线程
- 程序是一段静态的代码,进程是程序的一次动态执行过程,这个过程也是进程本身从产生、发展至消亡的过程。
- 线程不是进程,是比进程更小的执行单位。与进程不同的是,线程的中断和恢复可以更加节省系统的开销。一个进程在其执行过程中,会产生多个线程,线程间可以共享进程中的代码和数据。
- 没有进程就没有线程。
- Java中的线程
1.Java的多线程机制
- Java语言的一大特性点就是内置对多线程的支持。
- 多线程是指一个应用程序中同时存在几个执行本,按几条不同的执行线索共同工作的情况。
- 计算机在任何给定的时刻只能执行线程中的一个。Java虚拟机快速地把控制从一个线程切换到另一个线程,这些线程被轮流执行,使得每个线程都有机会使用CPU资源。
2.主线程(main线程)
- 每个Java应用程序都有一个缺省的主线程。
- 主线程(main线程)负责执行main方法。
- 如果main方法中没有创建其他的线程,那么当main方法执行完最后一个语句,即main方法返回时,JVM就会结束Java应用程序。
- 如果main方法中又创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,保证,每个线程都有机会使用CPU,main方法即使执行完最后的语句(主线程结束),JVM也不会结束Java应用程序,JVM一直要等到Java应用程序都结束之后,才结束Java应用程序。
3.线程的状态和生命周期
- Java语言使用
Thread
类及其子类的对象来表示线程。 - 新建的线程一般要经历4个状态:
- 新建:当一个
Thread
类或其子类的对象被声明并创建,新生的线程对象处于新建状态。此时它已经有了相应的内存空间和其他资源。 - 运行:JVM将CPU使用权切换给该线程时,此线程就可以脱离创建它的主线程独立开始自己的生命周期了。
当JVM将CPU使用权切换给该线程时,如果线程是Thread
的子类创建的,该类中的run()
方法就立刻执行,且程序必须在子类中重写父类的run()
方法。在线程没有结束run()
方法之前,不要让线程再调用start()
方法,否则将发生IllegalThreadStateException
异常。 - 中断:有四种原因的中断
- JVM将CPU资源从当前线程切换给其他线程,使本线程让出CPU的使用权处于中断状态。
- 线程使用CPU资源期间,执行了
sleep(int millsecond)
方法,使当前线程进入休眠状态。 - 线程使用CPU资源期间,执行了
wait()
方法,使得当前线程进入等待状态,必须由其他线程调用notify()
方法通知它,使得它重新进到线程队列中排队等待CPU资源,以便从中断处继续运行。 - 执行某个操作进入阻塞状态,比如执行读/写操作引起阻塞。只有当引起阻塞的原因消除时,线程才重新进到线程队列中排队等待CPU资源。
- 死亡:实质是线程释放了实体,即释放分配给线程对象的内存。
- 死亡原因有二:
- 正常运行的线程完成了它的全部工作,即执行完
run()
方法中的全部语句,结束了run()
方法; - 线程被提前强制性地终止,即强制
run()
方法结束。
- 正常运行的线程完成了它的全部工作,即执行完
- 新建:当一个
4.线程调度与优先级
- 每个Java线程的优先级都在常数1和10之间,即
Thread.MIN_PRIORITY
和Thread.MAX_PRIORITY
之间。如果没有明确地设置线程的优先级别,,每个线程的优先级都为常数5,即Thread.NORM_PRIORITY
。 - 可以通过
setPriority(int grade)
方法调整优先级,getPriority
方法返回线程的优先级。
- Thread
类与线程的创建
1.使用Thread
的子类
- 在编写
Thread
类的子类时,需要重写父类的run()
方法。其目的是规定线程的具体操作。
2.使用Thread
类
- Java不支持多继承,
Thread
类的子类不能再扩展其他的类。 - 用
Thread
类直接创建线程对象Thread(Runnable target)
,该构造方法中的参数是一个Runnable
类型的接口。 - 在创建线程对象时必须向构造方法的参数传递一个实现
Runnable
接口类的实例,该实例对象称作所创线程的目标对象,当线程调用start()
方法后,一旦轮到它来享用CPU资源,目标对象就会自动调用接口中的run()
方法(接口回调)。
3.目标对象与线程的关系
- 目标对象和线程完全解耦
- 目标对象组合线程(弱耦合)
4.关于run方法启动的次数
- 对于具有相同目标对象的线程,当其中一个线程享用CPU资源时,目标对象自动调用接口中的run方法,当轮到另一个线程享用CPU资源时,目标对象会再次调用接口中的run方法,
run()
方法中的局部变量会再次分配内存空间。即run()
方法已经启动运行了两次,分别运行在不同的线程中,即运行在不同的时间片内。
- 线程的常用方法
start()
:线程调用该方法将启动线程,使之从新建状态进入就绪队列排队,一旦轮到它来享用CPU资源时,就可以脱离创建它的线程独立开始自己的生命周期了。run()
:Thread
类的run()
方法与Runnable
接口中的run()
方法的功能和作用相同,都用来定义线程对象被调度之后所执行的操作,都是系统自动调用而用户程序不得引用的方法。sleep(int millsecond)
: 优先级高的线程可以在它的run()方法中调用sleep方法来使自己放弃CPU资源,休眠一段时间。isAlive()
: 线程处于“新建”状态时,线程调用isAlive()
方法返回false。在线程的run()方法结束之前,即没有进入死亡状态之前,线程调用isAlive()
方法返回true。currentThread()
:该方法是Thread
类中的类方法,可以用类名调用,该方法返回当前正在使用CPU资源的线程。interrupt()
:一个占有CPU资源的线程可以让休眠的线程调用interrupt()
方法“吵醒”自己,即导致休眠的线程发生InterruptedException
异常,从而结束休眠,重新排队等待CPU资源。
- 线程同步
- 所谓线程同步就是若干个线程都需要使用一个
synchronized(同步)
修饰的方法。 - 多个线程调用
synchronized
方法必须遵守同步机制。 - 线程同步机制:当一个线程A使用
synchronized
方法时,其他线程想使用这个synchronized
方法时就必须等待,直到线程A使用完该synchronized
方法。
- 协调同步的线程
- 当一个线程使用的同步方法中用到了某个变量,而此变量又需要其他线程修改后才能符合本线程的要求,则可以使用
wait()
方法。wait
方法可以中断线程的执行,使本线程等待,暂时让出CPU的使用权,并允许其他线程使用这个同步方法。 wait()
、notify()
、notifyAll()
都是Object类中的final方法,被所有的类继承且不允许重写得方法。特别需要注意的是,不可以在非同步方法中使用wait()
、notify()
、notifyAll()
。
- 线程联合
- 一个线程A在占有CPU资源期间,可以让其它线程调用join()和本线程联合,如:
B.join();
称A在运行期间联合了B。如果线程A在占有CPU资源期间一旦联合B线程,那么A线程将立刻中断执行,一直等到它联合的线程B执行完毕,A线程再重新排队等待CPU资源,以便恢复执行。如果A准备联合的B线程已经结束,那么B.join()
不会产生任何效果。
- GUI线程
- 当Java程序包含图形用户界面(GUI)时,Java虚拟机在运行应用程序会自动启动更多的线程
- 其中有两个重要的线程
AWT-EventQuecue
:负责处理GUI事件AWT-Windows
:负责将窗体或组件绘制到桌面
- 计时器线程
- Java提供了一个很方便的
Timer
类,该类在javax.swing
包中。 - 可以使用
Timer(int a,Object b);
创建计时器,a为毫秒,b为监视器(监视器必须是组件类),每隔a毫秒会执行b一次。 - 使用
Timer
类的方法start()
启动计时器,即启动线程。 - 使用
stop()
停止计时器,即挂起线程。 - 使用
restart()
重新启动计时器,即恢复线程。 - 计时器的监视器必须是组件类(例如
JFrame
、JButton
等)的子类的实例,否则计时器无法启动。
- 守护线程
- 线程默认是非守护线程,即用户(user)线程。
- 一个线程调用
void setDaemon(boolean on)
方法可以将自己设置成一个守护(Daemon)线程。
代码调试中的问题和解决过程
- 问题1:在运行课本例题12_1时,前后两次结果不一致。
- 问题1解决方案:课本解释:上述程序在不同的计算机或在同一台计算机反复运行的结果不尽相同,输出结果依赖当前CPU资源的使用情况
代码托管
xxx
xxx
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 156/156 | 1/1 | 20/20 | |
第二周 | 275/431 | 1/2 | 22/42 | |
第三周 | 761/1192 | 1/3 | 31/73 | |
第四周 | 428/1620 | 1/4 | 26/99 | |
第五周 | 264/1884 | 1/5 | 11/110 | |
第六周 | 1265/3149 | 4/9 | 32/142 | |
第七周 | 551/3700 | 2/11 | 25/167 | |
第八周 | 1105/4805 | 4/15 | 33/200 | |
第九周 | 1519/6324 | 2/17 | 30/230 | |
第十周 | 724/7048 | 5/22 | 22/252 |
尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。
-
计划学习时间:40小时
-
实际学习时间:32小时
-
改进情况:
(有空多看看现代软件工程 课件
软件工程师能力自我评价表)