• 201871010131-张兴盼《面向对象程序设计(java)》第十七周学习总结


    项目

    内容

    《面向对象程序设计(java)》

    https://home.cnblogs.com/u/nwnu-daizh/

    这个作业的要求在哪里

    https://www.cnblogs.com/nwnu-daizh/p/12073034.html

    作业学习目标

    (1) 理解和掌握线程的优先级属性及调度方法;

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

    (3) Java线程综合编程练习

    第一部分:总结线程同步技术

    1.多线程的概念:

    (1)多线程是进程执行过程中产生的多条执行线索。 

    (2)多线程意味着一个程序的多行语句可以看上去几 乎在同一时间内同时运行。 

    (3)线程不能独立存在,必须存在于进程中,同一进 程的各线程间共享进程空间的数据。 

    2.Java实现多线程有两种途径:
    创建Thread类的子类
    在程序中定义实现Runnable接口的类

    3.用Thread类的子类创建线程:

    (1)首先需从Thread类派生出一个子类,在该子类中 重写run()方法。 

    例: class hand extends Thread {

    public void run() {……}

    }

    (2) 然后用创建该子类的对象 

    Lefthand left=new Lefthand();

    Righthand right=new Righthand(); 

    (3)最后用start()方法启动线程 

    left.start();

    right.start();

    4.用Thread类的子类创建多线程的关键性操作:

    (1)定义Thread类的子类并实现用户线程操作,即 run()方法的实现。

    (2)在适当的时候启动线程。

    由于Java只支持单重继承,用这种方法定义的类不 可再继承其他父类。

    用Runnable()接口实现线程

    (1)首先设计一个实现Runnable接口的类; 

    (2) 然后在类中根据需要重写run方法; 

    (3)再创建该类对象,以此对象为参数建立Thread 类的对象; 

    (4)调用Thread类对象的start方法启动线程,将 CPU执行权转交到run方法。

    5.被阻塞线程和等待线程

     blocked (被阻塞):

    阻塞时线程不能进入队列排队,必须等到引起 阻塞的原因消除,才可重新进入排队队列。 

    sleep(),wait()是两个常用引起线程阻塞的方法。

    6.线程阻塞的三种情况:

    (1)等待阻塞 :通过调用线程的wait()方法,让线 程等待某工作的完成。 

    (2)同步阻塞 :线程在获取synchronized同步锁失 败(因为锁被其它线程所占用),它会进入同步阻 塞状态。 

    (3)其他阻塞 :通过调用线程的sleep()或join() 或发出了I/O请求时,线程会进入到阻塞状态。当 sleep()状态超时、join()等待线程终止或者超 时、或者I/O处理完毕时,线程重新转入就绪状态。

    7.被终止的线程

     Terminated (被终止) 线程被终止的原因有二: 

    (1)一是run()方法中最后一个语句执行完毕而自 然死亡。 

    (2)二是因为一个没有捕获的异常终止了run方法 而意外死亡。 

    可以调用线程的stop 方 法 杀 死 一 个 线 程 (thread.stop();),但是,stop方法已过时, 不要在自己的代码中调用它。

    8.其他判断和影响线程状态的方法:

    (1)join():等待指定线程的终止。 

    (2)join(long millis):经过指定时间等待终止指定 的线程。 

    (3)isAlive():测试当前线程是否在活动。 

    (4)yield():让当前线程由“运行状态”进入到“就 绪状态”,从而让其它具有相同优先级的等待线程 获取执行权。

    9.多线程调度

    (1)Java提供一个线程调度器来监控程序启动后进入可运行状态的所有线程。线程调度器按照线程的优先级决定应调度哪些线程来执行。

    (2)处于可运行状态的线程首先进入就绪队列排队等候处理器资源,同一时刻在就绪队列中的线程可能有多个。Java的多线程系统会给每个线程自动分配一个线程的优先级。

     10.Java 的线程调度采用优先级策略:

    (1)优先级高的先执行,优先级低的后执行;

    (2)多线程系统会自动为每个线程分配一个优先级,缺省时,继承其父类的优先级;

    (3)任务紧急的线程,其优先级较高;

    (4)同优先级的线程按“先进先出”的队列原则;

    11.守护线程

    守护线程的惟一用途是为其他线程提供服务。例 如计时线程。

    在一个线程启动之前,调用setDaemon方法可 将线程转换为守护线程(daemon thread)。 例如: setDaemon(true);

    12.要实现线程同步的原因: 

    java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时, 
    会产生冲突,使得变量值不唯一,因此我们需要加入同步锁来避免在当前线程的操作未完成前,其它线程改变共享资源值的情况发生,从而保证变量的唯一 
    性和准确性

    13.方法

    a.synchronized 

    synchronized可以修饰方法,但是由于同步本身就是一种高开销的操作,因此我们应该尽可能的减少同步的内容,提高性能 
    使用同步代码块,下面的方式

     1 synchronized (this){
     2                     if (account.getBalance() > money) {
     3                         System.out.println(Thread.currentThread().getName() + "取钱" + money + "成功");
     4                     try {
     5                         Thread.sleep(1);
     6                     } catch (InterruptedException e) {
     7                         throw new RuntimeException(e);
     8                     }
     9                         account.reduceBalance(money);
    10                         System.out.println("	" + Thread.currentThread().getName() + "成功后的余额: " + account.getBalance());
    11                     } else {
    12                         System.out.println(Thread.currentThread().getName() + "取钱失败");
    13                         System.out.println("	" + Thread.currentThread().getName() + "失败后的余额: " + account.getBalance());
    14                         break;
    15                     }
    16                 }

    b.volatile 

    volatile关键字保证了每个线程所取得的共享资源变量的值是一样的,它的原理是被volatile修饰的变量被访问时,都会从内存中读取,而不是从缓存(寄存器)中,这样保证了每个线程访问到的变量值的一致性 
    但是他不会保证操作的原子性

    1 private volatile double balance;

    c.java.util.concurrent包下的ReentrantLock类 
    使用的方法

     1 //需要声明这个锁
     2 private Lock lock = new ReentrantLock();
     3 
     4 // 存钱
     5 public void addMoney(int money) {
     6 lock.lock();//上锁
     7 try{
     8 count += money;
     9 System.out.println(System.currentTimeMillis() + "存进:" + money);
    10 
    11 }finally{
    12 lock.unlock();//解锁
    13 }
    14 }

    Lock与Synchronized的对比:
    主要相同点:Lock能完成Synchronized所实现的所有功能。
    主要不同点:Lock有比Synchronized更好的性能。Synchronized会自动释放锁,但是Lock一定要求程序员手工释放,并且必须在finally从句中释放。
    synchronized 修饰方法时 表示同一个对象在不同的线程中 表现为同步队列
    如果实例化不同的对象 那么synchronized就不会出现同步效果了。
    d.ThreadLocal 

    如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。现在明白了吧,原来每个线程运行的都是一个副本,也就是说存钱和取钱是两个账户,知识名字相同而已。
    这样的话当操作不一致的时候,就无法解决问题,会出现下面的结果

     1 public class Bank {
     2 
     3 private static ThreadLocal<Integer> count = new ThreadLocal<Integer>(){
     4 @Override
     5 protected Integer initialValue() {
     6 // TODO Auto-generated method stub
     7 return 0;
     8 }
     9 
    10 };
    11 // 存钱
    12 public void addMoney(int money) {
    13 count.set(count.get()+money);
    14 System.out.println(System.currentTimeMillis() + "存进:" + money);
    15 
    16 }
    17 
    18 // 取钱
    19 public void subMoney(int money) {
    20 if (count.get() - money < 0) {
    21 System.out.println("余额不足");
    22 return;
    23 }
    24 count.set(count.get()- money);
    25 System.out.println(+System.currentTimeMillis() + "取出:" + money);
    26 }
    27 
    28 // 查询
    29 public void lookMoney() {
    30 System.out.println("账户余额:" + count.get());
    31 }
    32 }
     1 package threadTest;
     2 
     3 
     4 public class SyncThreadTest {
     5 
     6 public static void main(String args[]){
     7 final Bank bank=new Bank();
     8 
     9 Thread tadd=new Thread(new Runnable() {
    10 
    11 @Override
    12 public void run() {
    13 // TODO Auto-generated method stub
    14 while(true){
    15 try {
    16 Thread.sleep(1000);
    17 } catch (InterruptedException e) {
    18 // TODO Auto-generated catch block
    19 e.printStackTrace();
    20 }
    21 bank.addMoney(100);
    22 bank.lookMoney();
    23 System.out.println("
    ");
    24 
    25 }
    26 }
    27 });
    28 
    29 Thread tsub = new Thread(new Runnable() {
    30 
    31 @Override
    32 public void run() {
    33 // TODO Auto-generated method stub
    34 while(true){
    35 bank.subMoney(100);
    36 bank.lookMoney();
    37 System.out.println("
    ");
    38 try {
    39 Thread.sleep(1000);
    40 } catch (InterruptedException e) {
    41 // TODO Auto-generated catch block
    42 e.printStackTrace();
    43 }
    44 }
    45 }
    46 });
    47 tsub.start();
    48 
    49 tadd.start();
    50 }
    51 }

    第二部分:实验部分

    1、实验目的与要求

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

    (2) 线程综合编程练习

    2、实验内容和步骤

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

    测试程序1:

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

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

    程序代码如下:

     1 package synch;
     2  
     3 import java.util.*;
     4 import java.util.concurrent.locks.*;
     5  
     6 /**
     7 一个银行有许多银行帐户,使用锁序列化访问 * @version 1.30 2004-08-01
     8  * @author Cay Horstmann
     9  */
    10 public class Bank
    11 {
    12    private final double[] accounts;
    13    private Lock bankLock;
    14    private Condition sufficientFunds;
    15  
    16    /**
    17     * 建设银行。
    18     * @param n 账号
    19     * @param initialBalance 每个账户的初始余额
    20     */
    21    public Bank(int n, double initialBalance)
    22    {
    23       accounts = new double[n];
    24       Arrays.fill(accounts, initialBalance);
    25       bankLock = new ReentrantLock();
    26       sufficientFunds = bankLock.newCondition();//在等待条件前,锁必须由当前线程保持。
    27    }
    28  
    29    /**
    30     * 把钱从一个账户转到另一个账户。
    31     * @param 从账户转账
    32     * @param 转到要转账的账户
    33     * @param 请允许我向你转达
    34     */
    35    public void transfer(int from, int to, double amount) throws InterruptedException
    36    {
    37       bankLock.lock();//加锁
    38       try
    39       {//锁对象引用条件对象
    40          while (accounts[from] < amount)
    41             sufficientFunds.await();//造成当前线程在接到信号或被中断之前一直处于等待状态。
    42          System.out.print(Thread.currentThread());
    43          accounts[from] -= amount;
    44          System.out.printf(" %10.2f from %d to %d", amount, from, to);
    45          accounts[to] += amount;
    46          System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
    47          sufficientFunds.signalAll();//如果所有的线程都在等待此条件,则唤醒所有线程
    48       }
    49       finally
    50       {
    51          bankLock.unlock();//解锁。
    52       }
    53    }
    54  
    55    /**
    56     * 获取所有帐户余额的总和。
    57     * @return 总余额
    58     */
    59    public double getTotalBalance()
    60    {
    61       bankLock.lock();
    62       try
    63       {
    64          double sum = 0;
    65  
    66          for (double a : accounts)
    67             sum += a;
    68  
    69          return sum;
    70       }
    71       finally
    72       {
    73          bankLock.unlock();
    74       }
    75    }
    76  
    77    /**
    78     * 获取银行中的帐户数量。
    79     * @return 账号
    80     */
    81    public int size()
    82    {
    83       return accounts.length;
    84    }
    85 }
     1 package synch;
     2  
     3 /**
     4  * 这个程序显示了多个线程如何安全地访问数据结构。
     5  * @version 1.31 2015-06-21
     6  * @author Cay Horstmann
     7  */
     8 public class SynchBankTest
     9 {
    10    public static final int NACCOUNTS = 100;
    11    public static final double INITIAL_BALANCE = 1000;
    12    public static final double MAX_AMOUNT = 1000;
    13    public static final int DELAY = 10;
    14     
    15    public static void main(String[] args)
    16    {
    17       Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);
    18       for (int i = 0; i < NACCOUNTS; i++)
    19       {
    20          int fromAccount = i;
    21          Runnable r = () -> {
    22             try
    23             {
    24                while (true)
    25                {
    26                   int toAccount = (int) (bank.size() * Math.random());
    27                   double amount = MAX_AMOUNT * Math.random();
    28                   bank.transfer(fromAccount, toAccount, amount);
    29                   Thread.sleep((int) (DELAY * Math.random()));//在指定的毫秒数内让当前正在执行的线程休眠
    30                }
    31             }
    32             catch (InterruptedException e)
    33             {
    34             }           
    35          };
    36          Thread t = new Thread(r);
    37          t.start();//使线程开始执行
    38       }
    39    }
    40 }

    运行结果如下:

    测试程序2:

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

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

    程序代码如下:

     1 package synch2;
     2 
     3 import java.util.*;
     4 
     5 /**
     6  * A bank with a number of bank accounts that uses synchronization primitives.
     7  * @version 1.30 2004-08-01
     8  * @author Cay Horstmann
     9  */
    10 public class Bank
    11 {
    12    private final double[] accounts;
    13 
    14    /**
    15     * Constructs the bank.
    16     * @param n the number of accounts
    17     * @param initialBalance the initial balance for each account
    18     */
    19    public Bank(int n, double initialBalance)
    20    {
    21       accounts = new double[n];
    22       Arrays.fill(accounts, initialBalance);
    23    }
    24 
    25    /**
    26     * Transfers money from one account to another.
    27     * @param from the account to transfer from
    28     * @param to the account to transfer to
    29     * @param amount the amount to transfer
    30     */
    31    public synchronized void transfer(int from, int to, double amount) throws InterruptedException//sysynchronized关键字修饰方法实现加锁操作
    32    {
    33       while (accounts[from] < amount)
    34          wait();  //wait()方法导致线程进入等待状态直到它被通知,该方法只能在一个同步方法中调用
    35       System.out.print(Thread.currentThread());//Thread.currentThread()返回当前执行线程的Thread对象
    36       accounts[from] -= amount;
    37       System.out.printf(" %10.2f from %d to %d", amount, from, to);
    38       accounts[to] += amount;
    39       System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
    40       notifyAll(); //解除那些在该对象上调用wait方法的线程的阻塞状态
    41    }
    42 
    43    /**
    44     * Gets the sum of all account balances.
    45     * @return the total balance
    46     */
    47    public synchronized double getTotalBalance()
    48    {
    49       double sum = 0;
    50 
    51       for (double a : accounts)
    52          sum += a;
    53 
    54       return sum;
    55    }
    56 
    57    /**
    58     * Gets the number of accounts in the bank.
    59     * @return the number of accounts
    60     */
    61    public int size()
    62    {
    63       return accounts.length;
    64    }
    65 }
     1 package synch2;
     2 
     3 /**
     4  * This program shows how multiple threads can safely access a data structure,
     5  * using synchronized methods.
     6  * @version 1.31 2015-06-21
     7  * @author Cay Horstmann
     8  */
     9 public class SynchBankTest2
    10 {
    11    public static final int NACCOUNTS = 100;
    12    public static final double INITIAL_BALANCE = 1000;
    13    public static final double MAX_AMOUNT = 1000;
    14    public static final int DELAY = 10;
    15 
    16    public static void main(String[] args)
    17    {
    18       Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);
    19       for (int i = 0; i < NACCOUNTS; i++)
    20       {
    21          int fromAccount = i;
    22          /**lamber表达式*/
    23          /*Runnable r = () -> {
    24             try
    25             {
    26                while (true)
    27                {
    28                   int toAccount = (int) (bank.size() * Math.random());
    29                   double amount = MAX_AMOUNT * Math.random();
    30                   bank.transfer(fromAccount, toAccount, amount);
    31                   Thread.sleep((int) (DELAY * Math.random()));
    32                }
    33             }
    34             catch (InterruptedException e)
    35             {
    36             }
    37          };*/
    38         /**直接声明
    39          *  Runnable r=new Runnable() {
    40             
    41             @Override
    42             public void run() {
    43                 try
    44                 {
    45                    while (true)
    46                    {
    47                       int toAccount = (int) (bank.size() * Math.random());
    48                       double amount = MAX_AMOUNT * Math.random();
    49                       bank.transfer(fromAccount, toAccount, amount);
    50                       Thread.sleep((int) (DELAY * Math.random()));
    51                    }
    52                 }
    53                 catch (InterruptedException e)
    54                 {
    55                 }
    56              };
    57                 
    58             
    59         };*/
    60          
    61          /**
    62                                   *  匿名内部类
    63                       */
    64          Thread t = new Thread(new Runnable(){
    65              
    66              @Override
    67              public void run() {
    68                  try
    69                  {
    70                     while (true)
    71                     {
    72                        int toAccount = (int) (bank.size() * Math.random());
    73                        double amount = MAX_AMOUNT * Math.random();
    74                        bank.transfer(fromAccount, toAccount, amount);
    75                        Thread.sleep((int) (DELAY * Math.random()));
    76                     }
    77                  }
    78                  catch (InterruptedException e)
    79                  {
    80                  }
    81               };
    82                  
    83              
    84          });
    85          t.start();
    86       }
    87    }
    88 }

    运行结果如下:

    测试程序3:

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

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

    程序代码如下:

     1 class Cbank
     2 {
     3      private static int s=2000;
     4      public   static void sub(int m)
     5      {
     6            int temp=s;
     7            temp=temp-m;
     8           try {
     9               Thread.sleep((int)(1000*Math.random()));
    10             }
    11            catch (InterruptedException e)  {              }
    12               s=temp;
    13               System.out.println("s="+s);
    14         }
    15     }
    16  
    17  
    18 class Customer extends Thread
    19 {
    20   public void run()
    21   {
    22    for( int i=1; i<=4; i++)
    23      Cbank.sub(100);
    24     }
    25  }
    26 public class Thread3
    27 {
    28  public static void main(String args[])
    29   {
    30    Customer customer1 = new Customer();
    31    Customer customer2 = new Customer();
    32    customer1.start();
    33    customer2.start();
    34   }
    35 }  

    运行结果如下:

     改进后代码如下:

     1 class Cbank
     2 {
     3      private static int s=2000;
     4      public  synchronized static void sub(int m)
     5      {
     6            int temp=s;
     7            temp=temp-m;
     8           try {
     9                  Thread.sleep((int)(1000*Math.random()));
    10                }
    11            catch (InterruptedException e)  {              }
    12               s=temp;
    13               System.out.println("s="+s);
    14           }
    15     }
    16  
    17  
    18 class Customer extends Thread
    19 {
    20   public void run()
    21   {
    22    for( int i=1; i<=4; i++)
    23      Cbank.sub(100);
    24     }
    25  }
    26  
    27 public class Thread3
    28 {
    29  public static void main(String args[])
    30   {
    31    Customer customer1 = new Customer();
    32    
    33    Customer customer2 = new Customer();
    34    customer1.start();
    35    customer2.start();
    36   }
    37 }

    运行截图如下:

    实验2:结对编程练习包含以下4部分

    结对伙伴:杨丽霞

    1)   程序设计思路简述;

        程序的主要设计思路没有太繁琐,只是新建三个售票口,在thread类中创建线程并开启线程,然后在run方法中定义线程任务,进行异常处理后设计在10张票数内时将售票情况进行打印,票数超过10张时程序结束;

    2)   符合编程规范的程序代码;

     1 package math;
     2  
     3 public class Demo {
     4     public static void main(String[] args) {
     5         Mythread mythread = new Mythread();
     6         Thread ticket1 = new Thread(mythread);
     7         Thread ticket2 = new Thread(mythread);
     8         Thread ticket3 = new Thread(mythread);//新建三个Thread类对象
     9         ticket1.start();
    10         ticket2.start();
    11         ticket3.start();//调用thread类的start方法来开启线程
    12     }
    13 }
    14  
    15 class Mythread implements Runnable {//实现runnable接口进行线程的创建
    16     int ticket = 1;
    17     boolean flag = true;
    18  
    19     @Override
    20     public void run() {//将线程任务代码定义到run方法中
    21         while (flag) {
    22             try {
    23                 Thread.sleep(500);
    24             } catch (InterruptedException e) {
    25                 // TODO Auto-generated catch block
    26                 e.printStackTrace();
    27             }
    28  
    29             synchronized (this) {
    30                 if (ticket <= 10) {
    31                     System.out.println(Thread.currentThread().getName() + "窗口售:第" + ticket + "张票");
    32                     ticket++;//票数在10张之内时,进行打印直到售出10张票时停止
    33                 }
    34                 if (ticket > 10) {
    35                     flag = false;
    36                 }
    37             }
    38         }
    39     }
    40  
    41 }

    3)   程序运行功能界面截图;

    三、实验总结:

            通过这次实验,我主要掌握了同步线程的相关问题,也学习了并发多线程,学习了锁对象、synchronized关键字等。在解读代码的过程中有好多不懂的代码,在程序测试出来后又通过对照运行结果,可以更加直观的体会有些代码的作用,加强对相关知识的理解,在平时要多加强动手能力,多思考。

  • 相关阅读:
    ios学习记录 day45 UI21 特效动画
    ios学习记录 day44 UI20 Storyboard 高级可视化编程 + 单例
    ios学习记录 day43 UI19 标签视图 和 block用法
    ios学习记录 day42 UI18 集合视图
    ios学习记录 day41 UI17 多线程
    7-2 How Many Ways to Buy a Piece of Land (25 分)
    7-1 Panda and PP Milk (20 分)
    1146 Topological Order (25 分)
    1089 Insert or Merge (25 分) 归并排序
    1098 Insertion or Heap Sort (25 分) 堆
  • 原文地址:https://www.cnblogs.com/zxp19990614/p/12088439.html
Copyright © 2020-2023  润新知