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

fairber fairber     2023-02-11     533

关键词:

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;

































































































张云飞201771010143《面对对象程序设计(java)》第十五周学习总结(代码片段)

JAR文件 Java程序的打包:程序编译完成后,程序员将.class文件压缩打包为.jar文件后,GUI界面程序就可以直接双击图标运行。 .jar文件(Java归档)既可以包含类文件,也可以包含诸如图像和声音这些其它类型的文件。 JA... 查看详情

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

1、实验目的与要求(1) 掌握线程同步的概念及实现技术; (2) 线程综合编程练习2、实验内容和步骤实验1:测试程序并进行代码注释。测试程序1:l 在Elipse环境下调试教材651页程序14-7,结合程序运行结果理解程序;... 查看详情

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

第一部分:课程准备部分填写课程学习平台注册账号,平台名称注册账号博客园:www.cnblogs.comhttps://www.cnblogs.com/Fairber/ 程序设计评测:https://pintia.cn/[email protected]代码托管平台:https://github.com/[email protected]中国大学MOOC... 查看详情

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

实验七继承附加实验实验时间2018-10-111、实验目的与要求(1)进一步理解4个成员访问权限修饰符的用途; 1.仅对本类可见-private2.对所有类可见-public3.对本包和所有子类可见-protected4.对本包可见-默认,不需要修饰符 掌握O... 查看详情

201771010143张云飞《面向对象程序设计(java)》第六章学习总结(代码片段)

实验六继承定义与使用实验时间2018-9-281、实验目的与要求(1) 理解继承的定义;特殊类的对象拥有一般类的全部属性与行为,称为特殊类对一般类的继承。一个类可以是多个一般类的特殊类,也可以从多个一般类中继承属性... 查看详情

beta冲刺scrummeeting4

...eting4第四天日期:2020/6/291.1今日完成任务情况。姓名任务张云飞编写压力测试,博客撰写张季跃编写压力测试,录制视频周强编写压力测试邹丰蔚编写压力测试         1.2成员贡献时间 成员时... 查看详情

java面对对象程序设计——面对对象(代码片段)

重点掌握1.类是对一类事物描述,是抽象的、概念上的定义;对象是实际存在的该类事物的每个个体,因而也称为实例2.创建对象的格式是:类名对象名称=new类名();3.方法的声明格式是:[<修饰符>]<返... 查看详情

面对对象的程序设计

面向对象(Object-Oriented,OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。 一、理解对象:        第一种:基于Object对象1varperson=newObject(... 查看详情

day-8:面对对象编程

  面对过程的程序设计方法意在将函数分成子函数,再依次调用这些函数来解决问题。  而面对对象的程序设计方法,来源于自然界,类是实例的抽象,实例是类的具体。自定义出来的对象是类,而所有的数据都可以看成是... 查看详情

冲刺博客

...:2020/6/191.1今日完成任务情况以及遇到的问题。成员任务张云飞对完成界面进行优化。周强数据库连接。张季跃程序安全性测试以及分析。邹丰蔚程序安全性测试以及分析。      遇到的问题: 我们团队... 查看详情

java面对对象程序设计——面对对象(代码片段)

重点掌握1.类是对一类事物描述,是抽象的、概念上的定义;对象是实际存在的该类事物的每个个体,因而也称为实例2.创建对象的格式是:类名对象名称=new类名();3.方法的声明格式是:[<修饰符>]<返... 查看详情

设计模式面对面之订阅模式

订阅模式订阅模式主要涉及到三种对象:订阅对象,发布对象,分发对象。案例没对这三种对象做抽像,大家可以根据应用场景去扩展。类图:常用的实现方式:订阅对象//订阅对象publicclassSubscribe{publicstringName;publicSubscribe(stringn... 查看详情

java面对对象入门-程序块初始化

Java实例初始化程序是在执行构造函数代码之前执行的代码块。每当我们创建一个新对象时,这些初始化程序就会运行。1.实例初始化语法用花括号创建实例初始化程序块。对象初始化语句写在括号内。publicclassDemoClass{//Thisisinitial... 查看详情

《设计模式之禅》笔记整理--面对对象设计六大原则

第一章、面对对象设计六大原则:(1)、单一职责原则:应该有且只有一个原因引起类的变更。为什么要用单一职责原则:(1)、类的复杂性降低,实现什么职责都有清晰明确的定义。           (2)、可读性提高,... 查看详情

java——面对对象

软件出现的目的:*用计算机的语言来描述世界*用计算机解决现实世界的问题面向对象的思想描述面向对象的世界面向对象设计和开发程序的好处:*交流更加流畅*提高设计和开发效率构造方法:构造方法是用来描述对象创建的... 查看详情

面对对象编程

面向对象编程标签(空格分隔):python基础---面向过程:根据业务逻辑从上到下写代码面向对象:将数据与函数绑定到一起,进行封装,这样能够更快速的开发程序,减少了重复代码的重写过程面向对象(object-oriented;简称:OO)至今... 查看详情

面对对象--继承(代码片段)

一:什么是继承:      在opp程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类,父类或者超类      1classCard:#定义了一个父类2def__init__(s... 查看详情

python支持面向对象吗?

...可以帮到你,谢谢!Python是一种面向对象、解释型计算机程序设计语言,由GuidovanRossum于1989年底发明,第一个公开发行版发行于1991年,Python源代码同样遵循GPL(GNUGeneralPublicLicense)协议。Python语法简洁而清晰,具有丰富和强大的类... 查看详情