zoukankan      html  css  js  c++  java
  • 张云飞 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接口创建线程的区别

    Java之多线程流学习总结-相关操作

        采用Runnable接口实现线程:

        优势:

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

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

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

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

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

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

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

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

    7、线程生命周期

    Java之多线程流学习总结-相关操作

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

        线程的生命周期分为创建(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;

  • 相关阅读:
    【HDOJ】2774 Shuffle
    【POJ】2170 Lattice Animals
    【POJ】1084 Square Destroyer
    【POJ】3523 The Morning after Halloween
    【POJ】3134 Power Calculus
    【Latex】如何在Latex中插入伪代码 —— clrscode3e
    【HDOJ】4801 Pocket Cube 的几种解法和优化
    【HDOJ】4080 Stammering Aliens
    【HDOJ】1800 Flying to the Mars
    SQL语法
  • 原文地址:https://www.cnblogs.com/fairber/p/10164167.html
Copyright © 2011-2022 走看看