zoukankan      html  css  js  c++  java
  • 201871010116-祁英红《面向对象程序设计(java)》第十七周学习总结

    博文正文开头格式:(2分)

    项目

    内容

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

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

    这个作业的要求在哪里

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

    作业学习目标

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

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

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

    随笔博文正文内容包括:

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

    线程同步

    多线程并发运行不确定性问题解决方案:引入线 程同步机制,使得另一线程要使用该方法,就只 能等待。

     在Java中解决多线程同步问题的方法有两种:

    1.- Java SE 5.0中引入ReentrantLock类。

    2.- 在共享内存的类方法前加synchronized修饰符。

    ……

    public synchronized static void sub(int m)

    ……

    解决方案一:锁对象与条件对象

    用ReentrantLock保护代码块的基本结构如下:

    myLock.lock();

    try {

       critical section

    } finally{

    myLock.unlock(); }

    有关锁对象和条件对象的关键要点:

    1、锁用来保护代码片段,保证任何时刻只能有一 个线程执行被保护的代码。

    2、锁管理试图进入被保护代码段的线程。

    3、锁可拥有一个或多个相关条件对象。

    4、每个条件对象管理那些已经进入被保护的代码 段但还不能运行的线程。

    解决方案二: synchronized关键字

    synchronized关键字作用:

    1、某个类内方法用synchronized 修饰后,该方法被称为同步方法;

    2、只要某个线程正在访问同步方法,其他线程欲要访问同步方法就被阻塞,直至线程从同步方法返回前唤醒被阻塞线程,其他线程方可能进入同步方法。

    3、一个线程在使用的同步方法中时,可能根据问题的需要,必须使用wait()方法使本线程等待,暂时让出CPU的使用权,并允许其它线程使用这个同步方法。

    4、线程如果用完同步方法,应当执行notifyAll()方 法通知所有由于使用这个同步方法而处于等待的 线程结束等待。

    对象锁

    (1)synchronized方法(对当前对象进行加锁)

    (2)synchronized代码块(对某一个对象进行加锁)

    如果要使用同步代码块必须设置一个要锁定的对象,所以一般可以锁定当前对象:this.

    (3)synchronized锁多对象

    当synchronized锁多个对象时不能实现同步操作,由此可以得出关键字synchronized取得的锁都是对象锁,而不是将一段代码或者方法(函数)当作锁。

    全局锁

    实现全局锁有两种方式:

    (1) 将synchronized关键字用在static方法上

    synchronized加到static静态方法上是对Class类上锁,而synchronized加到非static方法上是给对对象上锁。Class锁可以对类的所有对象实例起作用。

    (2) 用synchronized对类的Class对象进行上锁

    synchronized(class)代码块的作用与synchronized static方法的作用一样。

    多线程访问synchronized的八种方法:

    一:两个线程同时访问一个对象的 synchronized 方法,同步执行;

    二:两个线程访问的是两个对象的 synchronized 方法,并行执行;

    三:两个线程同时访问一个 synchronized 静态方法,同步执行;

    四:两个线程同时访问 synchronized 方法与非 synchronized 方法,并发执行;

    五:两个线程同时访问同一个对象的不同 synchronized 方法,同步执行;

    六:两个线程同时访问静态 synchronized 方法和非静态 synchronized 方法,并行执行;

    七:方法抛异常后,会释放锁;

    八:在 synchronized 方法中调用了普通方法,就不是线程安全的了,synchronized 的作用范围只在 “{}” 内;

    第二部分:实验部分

    实验1:测试程序1(5分)

    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);
          bankLock = new ReentrantLock();
          sufficientFunds = bankLock.newCondition();//在等待条件前,锁必须由当前线程保持。
       }
    
       /**
        * 把钱从一个账户转到另一个账户。
        * @param 从账户转账
        * @param 转到要转账的账户
        * @param 请允许我向你转达
        */
       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;
       }
    }
    

      

    package synch;
    
    /**
     * 这个程序显示了多个线程如何安全地访问数据结构。
     * @version 1.31 2015-06-21
     * @author Cay Horstmann
     */
    public class SynchBankTest
    {
       public static final int NACCOUNTS = 100;
       public static final double INITIAL_BALANCE = 1000;
       public static final double MAX_AMOUNT = 1000;
       public static final int DELAY = 10;
       
       public static void main(String[] args)
       {
          Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);
          for (int i = 0; i < NACCOUNTS; i++)
          {
             int fromAccount = i;
             Runnable r = () -> {
                try
                {
                   while (true)
                   {
                      int toAccount = (int) (bank.size() * Math.random());
                      double amount = MAX_AMOUNT * Math.random();
                      bank.transfer(fromAccount, toAccount, amount);
                      Thread.sleep((int) (DELAY * Math.random()));//在指定的毫秒数内让当前正在执行的线程休眠
                   }
                }
                catch (InterruptedException e)
                {
                }            
             };
             Thread t = new Thread(r);
             t.start();//使线程开始执行
          }
       }
    }
    

      运行结果如下:

    有关锁对象和条件对象的关键要点:

    1、锁用来保护代码片段,保证任何时刻只能有一 个线程执行被保护的代码。

    2、锁管理试图进入被保护代码段的线程。

    3、锁可拥有一个或多个相关条件对象。

    4、每个条件对象管理那些已经进入被保护的代码 段但还不能运行的线程。

    实验1:测试程序2(5分)

    package synch2;
    
    import java.util.*;
    
    /**
     * 具有多个使用同步原语的银行账户的银行。
     * @version 1.30 2004-08-01
     * @author Cay Horstmann
     */
    public class Bank
    {
       private final double[] accounts;
    
       /**
        * 建设银行。
        * @param n 账号
        * @param initialBalance 每个账户的初始余额
        */
       public Bank(int n, double initialBalance)
       {
          accounts = new double[n];
          Arrays.fill(accounts, initialBalance);
       }
    
       /**
        * 把钱从一个账户转到另一个账户。
        * @param 从账户转账
        * @param 转到要转账的账户
        * @param 请允许我向你转达
        */
       public synchronized void transfer(int from, int to, double amount) throws InterruptedException
       {
          while (accounts[from] < amount)
             wait();//添加一个线程到等待一个集中
          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());
          notifyAll();//解除当前线程的阻塞状态
       }
    
       /**
        * 获取所有帐户余额的总和。
        * @return 总余额
        */
       public synchronized double getTotalBalance()
       {
          double sum = 0;
    
          for (double a : accounts)
             sum += a;
    
          return sum;
       }
    
       /**
        * 获取银行中的帐户数量。
        * @return 
        */
       public int size()
       {
          return accounts.length;
       }
    }
    

      

    package synch2;
    
    /**
     * 
     * 这个程序展示了多个线程如何使用同步方法安全地访问数据结构。
     * @version 1.31 2015-06-21
     * @author Cay Horstmann
     */
    public class SynchBankTest2
    {
       public static final int NACCOUNTS = 100;
       public static final double INITIAL_BALANCE = 1000;
       public static final double MAX_AMOUNT = 1000;
       public static final int DELAY = 10;
    
       public static void main(String[] args)
       {
          Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);
          for (int i = 0; i < NACCOUNTS; i++)
          {
             int fromAccount = i;//内部类的使用
             Runnable r = () -> {
                try
                {
                	while (true)
                    {
                       int toAccount = (int) (bank.size() * Math.random());//拿出一个随机账户
                       double amount = MAX_AMOUNT * Math.random();//设定随机一笔钱
                       bank.transfer(fromAccount, toAccount, amount);//转账操作
                       Thread.sleep((int) (DELAY * Math.random()));//随机休眠时间
                    }
                }
                catch (InterruptedException e)//抛出异常
                {
                }
             };
             Thread t = new Thread(r);
             t.start();
          }
       }
    }
    

      运行结果如下:

    多线程访问synchronized的八种方法:

    一:两个线程同时访问一个对象的 synchronized 方法,同步执行;

    二:两个线程访问的是两个对象的 synchronized 方法,并行执行;

    三:两个线程同时访问一个 synchronized 静态方法,同步执行;

    四:两个线程同时访问 synchronized 方法与非 synchronized 方法,并发执行;

    五:两个线程同时访问同一个对象的不同 synchronized 方法,同步执行;

    六:两个线程同时访问静态 synchronized 方法和非静态 synchronized 方法,并行执行;

    七:方法抛异常后,会释放锁;

    八:在 synchronized 方法中调用了普通方法,就不是线程安全的了,synchronized 的作用范围只在 “{}” 内;

    实验1:测试程序3(5分)

    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();
      }
    }  

    运行结果如下:

    改进后代码如下:

    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:结对编程练习包含以下4部分(10分)

    结对伙伴:李华

    1)   程序设计思路简述;

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

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

    package math;
    
    public class Demo {
        public static void main(String[] args) {
            Mythread mythread = new Mythread();
            Thread ticket1 = new Thread(mythread);
            Thread ticket2 = new Thread(mythread);
            Thread ticket3 = new Thread(mythread);//新建三个Thread类对象
            ticket1.start();
            ticket2.start();
            ticket3.start();//调用thread类的start方法来开启线程
        }
    }
    
    class Mythread implements Runnable {//实现runnable接口进行线程的创建
        int ticket = 1;
        boolean flag = true;
    
        @Override
        public void run() {//将线程任务代码定义到run方法中
            while (flag) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
    
                synchronized (this) {
                    if (ticket <= 10) {
                        System.out.println(Thread.currentThread().getName() + "窗口售:第" + ticket + "张票");
                        ticket++;//票数在10张之内时,进行打印直到售出10张票时停止
                    }
                    if (ticket > 10) {
                        flag = false;
                    }
                }
            }
        }
    
    }

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

     

    实验总结:(5分)

    在本周的学习中,我学习了线程同步这一知识点,我了解到这一知识点是用来解决多线程并发运行不确定性问题。学习了解决多线程同步问题的两种方案,分别是锁对象与条件对象引用和 synchronized关键字。在实验课上收获了当注释调代码sufficientFunds.await();会出现死锁状态等知识,感受颇多。注意点有:线程如果用完同步方法,应当执行notifyAll()方 法通知所有由于使用这个同步方法而处于等待的 线程结束等待。线程如果用完同步方法,应当执行notifyAll()方 法通知所有由于使用这个同步方法而处于等待的 线程结束等待。以后会坚持学习Java。

  • 相关阅读:
    买房的贷款时间是否是越长越好?https://www.zhihu.com/question/20842791
    asp.net cookie and session
    leelazero and google colab
    download file by python in google colab
    physical processor, core, logical processor
    通过powershell操作eventlog
    openxml in sql server
    get the page name from url
    How to Execute Page_Load() in Page's Base Class?
    Difference between HttpContext.Request and Request
  • 原文地址:https://www.cnblogs.com/qyhq/p/12078285.html
Copyright © 2011-2022 走看看