张云飞 201771010143 《面对对象程序设计(java)》第十七周学习总结

1、实验目的与要求

(1) 掌握线程同步的概念及实现技术;

(2) 线程综合编程练习

2、实验内容和步骤

实验1:测试程序并进行代码注释。

测试程序1:

l 在Elipse环境下调试教材651页程序14-7,结合程序运行结果理解程序;

l 掌握利用锁对象和条件对象实现的多线程同步技术。

package synch;

import java.util.*;
import java.util.concurrent.locks.*;

/**
* 有许多银行账户的银行,它使用锁来序列化访问
* @version 1.30 2004-08-01
* @author Cay Horstmann
*/
public class Bank
{
private final double[] accounts;
private Lock bankLock;
private Condition sufficientFunds;

/**
* 建造银行
* @param n 帐户数量
* @param initialBalance 每个帐户的初始余额
*/
public Bank(int n, double initialBalance)
{
accounts = new double[n];
Arrays.fill(accounts, initialBalance);//将initialBalance分配给accounts数组的每个元素。
bankLock = new ReentrantLock();
sufficientFunds = bankLock.newCondition();//返回绑定到此 Lock 实例的新 Condition 实例
}

/**
* 将资金从一个帐户转移到另一个帐户
* @param from 帐户转帐来自
* @param to 帐户转帐到
* @param amount 转账金额
*/
public void transfer(int from, int to, double amount) throws InterruptedException
{
bankLock.lock();
try
{
while (accounts[from] < amount)
sufficientFunds.await();//将该线程放在条件的等待集中
System.out.print(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f from %d to %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
sufficientFunds.signalAll();//唤醒所有等待线程
}
finally
{
bankLock.unlock();
}
}

/**
* 获取所有帐户余额的总和
* @return 总余额
*/
public double getTotalBalance()
{
bankLock.lock();
try
{
double sum = 0;

for (double a : accounts)
sum += a;

return sum;
}
finally
{
bankLock.unlock();
}
}

/**
* 获取银行中的帐户数量
* @return 帐户数量
*/
public int size()
{
return accounts.length;
}
}

Bank

实验结果:

测试程序2:

l 在Elipse环境下调试教材655页程序14-8,结合程序运行结果理解程序;

l 掌握synchronized在多线程同步中的应用。

实验结果:

测试程序3:

l 在Elipse环境下运行以下程序,结合程序运行结果分析程序存在问题;

l 尝试解决程序中存在问题。

class Cbank

{

private static int s=2000;

public   static void sub(int m)

{

int temp=s;

temp=temp-m;

try {

Thread.sleep((int)(1000*Math.random()));

}

catch (InterruptedException e)  {              }

s=temp;

System.out.println("s="+s);

}

}

class Customer extends Thread

{

public void run()

{

for( int i=1; i<=4; i++)

Cbank.sub(100);

}

}

public class Thread3

{

public static void main(String args[])

{

Customer customer1 = new Customer();

Customer customer2 = new Customer();

customer1.start();

customer2.start();

}

}

修改后的代码:

package a;

class Cbank

{

private static int s=2000;

public synchronized static void sub(int m)

{

int temp=s;

temp=temp-m;

try {

Thread.sleep((int)(1000*Math.random()));

}

catch (InterruptedException e) { }

s=temp;

System.out.println("s="+s);

}

}

class Customer extends Thread

{

public void run()

{

for( int i=1; i<=4; i++)

Cbank.sub(100);

}

}

public class Thread3

{

public static void main(String args[])

{

Customer customer1 = new Customer();

Customer customer2 = new Customer();

customer1.start();

customer2.start();

}

}

实验2 编程练习

利用多线程及同步方法,编写一个程序模拟火车票售票系统,共3个窗口,卖10张票,程序输出结果类似(程序输出不唯一,可以是其他类似结果)。

Thread-0窗口售:第1张票

Thread-0窗口售:第2张票

Thread-1窗口售:第3张票

Thread-2窗口售:第4张票

Thread-2窗口售:第5张票

Thread-1窗口售:第6张票

Thread-0窗口售:第7张票

Thread-2窗口售:第8张票

Thread-1窗口售:第9张票

Thread-0窗口售:第10张票

代码:

public class Demo {

public static void main(String[] args) {
// TODO Auto-generated method stub
Mythread mythread = new Mythread();
Thread t1 = new Thread(mythread);
Thread t2 = new Thread(mythread);
Thread t3 = new Thread(mythread);
t1.start();
t2.start();
t3.start();
}
}

class Mythread implements Runnable {
int x = 1;
boolean f = true;

public void run() {
while (f) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (this) {
if (x <= 10) {
System.out.println(Thread.currentThread().getName() + "窗口售:第" + x + "张票");
x++;
} else
f = false;
}

}

}
}

Demo

结果:

本周学习总结:

5、线程的创建和启动

A、[重点]继承Thread类或实现Runnable接口,重写或实现run方法,run方法代表线程要完成的任务

B、创建Thread子类或是Runnable的实现类,即创建的线程对象;不同的是接口实现线程,

需要将接口的实现类作为参数传递给Thread类的构造参数

C、用线程对象的start方法启动线程

6、继承Thread和实现Runnable接口创建线程的区别

采用Runnable接口实现线程:

优势:

A、线程类只是实现了Runnable接口,还可以继承其他的类

B、在这种方式下,可以多个线程共享同一个目标对象,所以很合适多个线程来处理同一份资源的情况,

从而可以将CPU、代码和数据分开,形成清晰的模型,较好的面相对象思想。

劣势:编程稍微复杂,如果需要访问当前线程需要用Thread.currentThread方法来获取

采用继承Thread类的方式实现线程:

优势:编写简单,如果要获得当前线程直接this即可

劣势:线程类继承了Thread,不能在继承其他类

相对而言,用Runnable的方式更好,具体可以根据当前需要而定;

7、线程生命周期

线程被创建启动后,不并不是启动后就进入了执行状态,也不是一直处于的执行状态。

线程的生命周期分为创建(new)、就绪(Runnable)、运行(running)、阻塞(Blocked)、死亡(Dead)五种状态。

线程启动后不会一直霸占CPU资源,所以CPU需要在多条线程中切换执行,线程就会在多次的运行和阻塞中切换。

8、新建(new)和就绪(Runnable)状态

当new一个线程后,该线程处于新建状态,此时它和Java对象一样,仅仅由Java虚拟机为其分配内存空间,并初始化成员变量。

此时线程对象没有表现出任何的动态特征,程序也不会执行线程的执行体。

注意:run方法是线程的执行体,不能由我们手动调用。我们可以用start方法启动线程,系统会把run方法当成线程的执行体来运行,

如果直接调用线程对象run方法,则run方法立即会被运行。而且在run方法返回之前其他线程无法并行执行,

也就是说系统会把当前线程类当成一个普通的Java对象,而run方法也是一个普通的方法,而不是线程的执行体。

9、运行(running)和阻塞(Blocked)状态

如果处于就绪状态的线程就获得了CPU,开始执行run方法的线程执行体,则该线程处于运行状态。

单CPU的机器,任何时刻只有一条线程处于运行状态。当然,在多CPU机器上将会有多线程并行(parallel)执行,

当线程大于CPU数量时,依然会在同一个CPU上切换执行。

线程运行机制:一个线程运行后,它不可能一直处于运行状态(除非它执行的时间很短,瞬间执行完成),线程在运行过程中需要中断,

目的是让其他的线程有运行机会,线程的调度取决于底层的策略。对应抢占式的系统而言,系统会给每个可执行的线程一个小时间段来处理任务,

当时间段到达系统就会剥夺该线程的资源,让其他的线程有运行的机会。在选择下一个线程时,系统会考虑线程优先级。

以下情况会出现线程阻塞状态:

A、线程调用sleep方法,主动放弃占用的处理器资源

B、线程调用了阻塞式IO方法,在该方法返回前,该线程被阻塞

C、线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有。

D、线程等待某个通知(notify)

E、程序调用了suspend方法将该线程挂起。不过这个方法容易导致死锁,尽量不免使用该方法

当线程被阻塞后,其他线程将有机会执行。被阻塞的线程会在合适的时候重新进入就绪状态,注意是就绪状态不是运行状态。

也就是被阻塞线程在阻塞解除后,必须重新等待线程调度器再次调用它。

针对上面线程阻塞的情况,发生以下特定的情况可以解除阻塞,让进程进入就绪状态:

A、调用sleep方法的经过了指定的休眠时间

B、线程调用的阻塞IO已经返回,阻塞方法执行完毕

C、线程成功获得了试图同步的监视器

D、线程正在等待某个通知,其他线程发出了通知

E、处于挂起状态的线程调用了resume恢复方法

线程从阻塞状态只能进入就绪状态,无法进入运行状态。而就绪和运行状态之间的转换通常不受程序控制,而是由系统调度所致的。

当就绪状态的线程获得资源时,该线程进入运行状态;当运行状态的线程事情处理器资源时就进入了就绪状态。

但对调用了yield的方法就例外,此方法可以让运行状态转入就绪状态。

10、线程死亡(Dead)状态

线程会在以下方式进入死亡状态:

A、run方法执行完成,线程正常结束

B、线程抛出未捕获的异常或Error

C、直接调用该线程的stop方法来结束线程—该方法易导致死锁,注意使用

注意:当主线程结束的时候,其他线程不受任何影响。一旦子线程启动后,会拥有和主线程相同的地位,不受主线程影响。

isAlive方法可以测试当前线程是否死亡,当线程处于就绪、运行、阻塞状态,该方法返回true,如果线程处于新建或死亡状态就会返回false。

不要试图对死亡的线程调用start方法,来启动它。死亡线程不可能再次运行。

11、控制线程

Java线程提供了很多工具方法,这些方法都很好的控制线程

A、join线程

让一个线程等待另一个线程完成的方法。当某个程序执行流中调用其他线程的join方法时,调用线程将会被阻塞,直到被join方法的join线程执行完成为止。

join方法通常有使用线程的程序调用,将大问题划分成许多小问题。每个小问题分配一个线程。当所有的小问题得到处理后,再调用主线程进一步操作。

join有三种重载模式:

一、join等待被join的线程执行完成

二、join(long millis)等待被join的线程时间最长为millis毫秒,如果在millis毫秒外,被join的线程还没有执行完则不再等待

三、join(long millis, int nanos)被join的线程等待时间长为millis毫秒加上nanos微秒

通常我们很少用第三种join,原因有二:程序对时间的精度无需精确到千分之一毫秒

计算机硬件、操作系统也无法做到精确到千分之一毫秒

B、后台线程

有一种线程,在后台运行,它的任务是为其他线程提供服务,这种线程被称为“后台线程(Daemon Thread)”,有被称为“守护线程”或“精灵线程”。

JVM的垃圾回收器线程就是后台进程。

后台进程有个特征是:如果前台的进程都死亡,那么后台进程也死亡。(它为前台进程服务)

用Thread的setDaemon (true)方法可以指定当前线程为后台线程。

注意:前台线程执行完成死亡后,JVM会通知后台线程,后台线程就会死亡。但它得到通知到后台线程作成响应,需要一段时间,

而且要将某个线程设置为后台线程,必需要在该线程启动前设置,也就是说设置setDaemon必需在start方法前面调用。

否则会出现java.lang.IllegalThreadStateException异常

C、线程休眠sleep

如果需要当前线程暂停一段时间,并进入阻塞状态就需要用sleep,sleep有2中重载方式:

sleep(long millis)让当前线程暂停millis毫秒后,并进入阻塞状态,该方法受系统计时器和线程调度器的影响

sleep(long millis, int nanos)让当前正在执行的线程暂停millis毫秒+nanos微秒,并进入阻塞

当调用sleep方法进入阻塞状态后,在sleep时间段内,该线程不会获得执行机会,即使没有其他可运行的线程,处于sleep的线程不会执行。

D、线程让步yield

yield和sleep有点类似,它也可以让当前执行的线程暂停,但它不会阻塞线程,只是将该线程转入到就绪状态。

yield只是让当前线程暂停下,让系统线程调度器重新调度下。

当yield的线程后,当前线程暂停。系统线程调度器会让优先级相同或是更高的线程运行。

sleep和yield的区别

(1)、sleep方法暂停当前线程后,会给其他线程执行集合,不会理会线程的优先级。但yield则会给优先级相同或高优先级的线程执行机会

(2)、sleep方法会将线程转入阻塞状态,直到经过阻塞时间才会转入到就绪状态;而yield则不会将线程转入到阻塞状态,它只是强制当前线程进入就绪状态。

因此完全有可能调用yield方法暂停之后,立即再次获得处理器资源继续运行。

(3)、sleep声明抛出了InterruptedException异常,所以调用sleep方法时,要么捕获异常,要么抛出异常。而yield没有申明抛出任何异常

E、改变线程优先级

每个线程都有优先级,优先级决定线程的运行机会的多少。

每个线程默认和它创建的父类的优先级相同,main方法的优先级是普通优先级,那在main方法中创建的子线程都是普通优先级。

getPriority(int newPriority)/setPriority(int)

设置优先级有以下级别:

MAX_PRIORITY 值是10

MIN_PRIORITY 值是1

NORM_PRIORITY 值是5

范围是1-10;

posted on 2018-12-23 14:08 Fairber 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/fairber/p/10164167.html

张云飞 201771010143 《面对对象程序设计(java)》第十七周学习总结相关推荐

  1. 201671010117 2016-2017-2 《Java程序设计》Java第十七周学习心得

    Java第十七周学习心得 这一周老师对线程的内容进行了详细讲解,包括线程创建的两种技术,和线程的优先级属性及调度方法,对于布置的实验作业中,实验九中存在一点问题,程序运行不出来,后来跟同学商量探讨没有 ...

  2. 201871010115——马北《面向对象程序设计JAVA》第二周学习总结

    项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...

  3. 面对对象程序设计---第一次blog

    这里写自定义目录标题 引言: 第一次与第二次大作业 题目集2:7-2 串口字符解析 点线形系列1-计算两点之间的距离 点线形系列2-线的计算 点线形系列3-三角形的计算 总结 引言: 这次的作业是基础 ...

  4. 20175227张雪莹 2018-2019-2 《Java程序设计》第三周学习总结

    20175227张雪莹 2018-2019-2 <Java程序设计>第三周学习总结 教材学习内容总结 (仅在此列举个性化学习总结) 一.编程语言的几个发展阶段. 1.面向机器语言:汇编语言 ...

  5. 20175305张天钰《java程序设计》第七周学习总结

    <java程序设计>第七周学习总结 第八章 常用实用类 1.String类 1.String类不可以有子类.2.用户无法输出String对象的引用,输出的是字符序列 3.构造方法:Stri ...

  6. 20175204 张湲祯 2018-2019-2《Java程序设计》第三周学习总结

    20175204 张湲祯 2018-2019-2<Java程序设计>第三周学习总结 教材学习内容总结 -第四章类与对象要点: -面向对象语言三个特性:封装性:继承:多态: -类:1.类是组 ...

  7. 20175227张雪莹 2018-2019-2 《Java程序设计》第十一周学习总结

    20175227张雪莹 2018-2019-2 <Java程序设计>第十一周学习总结 教材学习内容总结 第十三章 Java网络编程 URL类 一个URL对象通常包含最基本的三部分信息:协议 ...

  8. 20175227张雪莹 2018-2019-2 《Java程序设计》第五周学习总结

    20175227张雪莹 2018-2019-2 <Java程序设计>第五周学习总结 教材学习内容总结 第六章接口与实现 接口 接口体中所有的常量访问权限一定是public和static(可 ...

  9. 20175227张雪莹 2018-2019-2 《Java程序设计》第六周学习总结

    20175227张雪莹 2018-2019-2 <Java程序设计>第六周学习总结 教材学习内容总结 第七章 内部类与异常类 内部类:在一个类中定义另一个类:包含内部类的类为外嵌类 内部类 ...

最新文章

  1. 2019计算机考研学硕,2019计算机考研选学硕、专硕、非全日制研究生哪个好?
  2. 一个很不错的让ie6的js兼容 ie7,ie8的库
  3. vue router按需加载
  4. Uva10191 复合词
  5. UA OPTI501 电磁波8 麦克斯韦方程边界条件的推导
  6. 2.4 嵌入矩阵-深度学习第五课《序列模型》-Stanford吴恩达教授
  7. 使用shell进行mvn打包,根据mvn是否打包成功来进行下一步操作
  8. ib课程计算机科学内容,新的IB计算机课程主要学习什么内容?有什么新的特点呢?...
  9. 如何用SAP WebIDE的Fiori创建向导基于ABAP OData service快速创建UI5应用
  10. mysql 字符串类型 char varchar
  11. Redis Config Set 命令
  12. Moodle插件开发笔记
  13. 剑指offer例题5—逆序输出链表
  14. 苹果 CEO 库克“喜当爹”,被女子索赔31.6亿分手费!
  15. 圆形刻度盘 进度 展示
  16. Zoi选项 — Insure抑制功能
  17. “AI”与“爱”满格下的百度地图:刻画真实世界,社会责任同行
  18. 外卖和快递行业数据_白领市场三分天下,外卖行业将何去何从?
  19. fastdfs断点续传
  20. STP配置 HSRP配置 端口追踪

热门文章

  1. plsql查询不显示结果_管理NVivo的查询结果
  2. 卸载vuecli3_112、vue-cli3安装遇到的问题,卸载不掉旧版本,导致更新不了
  3. elmentui的短信验证界面_[javascript] elementui下login登录页界面和js验证逻辑
  4. IntelliJ IDEA设置代码提示(常用快捷键)
  5. 腾讯云首发智能网关流控,公有云进入网络精细管控时代
  6. Ubuntu安装配置JDK、Tomcat、SVN服务器
  7. 『资源』机器学习实践中文资源合集
  8. Windows下调试hadoop
  9. 使用php下载的文件打不开,自己用着没问题,客户用就不行?
  10. 十二、流程控制之条件运算符