201871010121-王方-《面向对象程序设计java》第十七周学习总结

项目

内容

这个作业属于哪个课程

https://www.cnblogs.com/nwnu-daizh

这个作业的要求在哪里

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

 

 作业学习目标

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

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

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

 

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

一、synchronized关键字
在Java语言中,每个对象都有一个对象锁与之相关联,该锁表明对象在任何时候只允许被一个线程锁拥有,当一个线程调用对象的一段synchronized代码时,需要先获取这个锁,然后去执行相应的代码,执行结束之后,释放锁。
而synchronized关键字主要有两种用法:synchronized方法和synchronized块。此外,这个关键字还可以作用于静态方法、类或者某个实例,但这都对程序的效率有很大的影响。
1.synchronized方法。在方法的声明前主要有synchronized关键字,示例如下:

 public synchronized void mutiThreadAccess();

只要把多个线程对类需要被同步的资源的操作放到mutiThreadAccess()方法中,就能保证这个方法在同一时刻只能被同一个线程访问,从而保证了多线程访问的安全性。然而,当一个方法的方法体规模非常大时,把该方法声明为synchronized会大大影响程序的执行效率。为了提高程序的效率,Java提供了synchronized块。

 2.synchronized块

 synchronized块既可以把任意的代码段声明为synchronized,也可以指定上锁的对象。其用法如下:

synchronized(syncObject){

   //访问synchObject的代码
}
二、wait()方法与notify()方法
当使用synchronized来修饰某个共享资源时,如果线程A1在执行synchronized代码,另一个线程A2也要同时执行同一个对象的同一synchronized代码时,线程A2将要等到线程A1执行完成之后,才能继续执行。在这种情况下可以使用wait()方法和notify()方法。
在synchronized代码被执行期间,线程可以调用对象的wait()方法,释放对象锁,进入等待状态,并且可以调用notify()方法或者notifyAll()方法通知正在等待的其他线程。notify()方法仅唤醒一个线程(即等待队列中的第一个线程),并允许它去获得锁,notifyAll()方法唤醒所有等待这个对象的线程并允许他们去获得锁,但并不是让所有唤醒线程都去获取到锁,而是让他们去竞争。
三、Lock
JDK5新增加了Lock接口以及它的一个实现类ReentrantLock(重入锁),Lock也可以用来实现多线程的同步。具体而言,它提供了如下一些方法来实现多线程的同步:
1.lock()。
这个方法以阻塞的方式获取锁,也就是说,如果获取到锁了,立即返回;如果别的线程持有锁,当前线程就等待,知道获取到锁之后返回。
2.tryLock()。
这个方法与lock()方法不同,它以非阻塞的方式来获取锁。此外,它只是常识性地去获取一下锁,如果获取到了锁,立即返回true,否则立即返回false。
3.tryLock(long timeout,TimeUnit unit)
如果获取到锁,立即返回true,否则会等待参数给定的时间单元,在等待的过程中,如果获取到了锁,就返回true,如果等待超时则返回false。
4.lockInterruptibly()
如果获取了锁,立即返回;如果没有获取到锁,当前线程处于休眠状态,直到获取到锁,或者当前线程被别的线程中断(会受到InterruptedException异常)。它与lock()方法最大的区别在于如果lock()方法获取不到锁就会一直处于阻塞状态,而且还会忽略interrupt()方法。
示例为:
package javatest; 
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
    public class LockTest {     
    public static void main(String[] args)throws InterruptedException {        
// TODO Auto-generated method stub        
    final Lock lock=new ReentrantLock();        
    lock.lock();        
    Thread t1=new Thread(new Runnable() {            
    public void run() {                
       try {                    
         lock.lockInterruptibly();                
         }
         catch(InterruptedException e)                
        {                             
            System.out.println("interrupted");                
         }            
       }        
 });        
       t1.start();        
       t1.interrupt();        
       Thread.sleep(1);    
}
}

如果把lock.lockInterruptibly()替换为lock.lock(),编译器将会提示lock.lock()catch代码块无效,这是因为lock.lock()不会抛出异常,由此可见lock()方法会忽略interrupt()引发的异常。

 第二部分:实验总结

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

测试程序1:

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

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

程序源代码为:

synch 

package synch;

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

/**
 * A bank with a number of bank accounts that uses locks for serializing access.
 * @version 1.30 2004-08-01
 * @author Cay Horstmann
 */
public class Bank
{
   private final double[] accounts;
   private Lock bankLock;//加锁
   private Condition sufficientFunds;//有效条件

   /**
    * Constructs the bank.
    * @param n the number of accounts
    * @param initialBalance the initial balance for each account
    */
   public Bank(int n, double initialBalance)
   {
      accounts = new double[n];
      Arrays.fill(accounts, initialBalance);
      bankLock = new ReentrantLock();
      sufficientFunds = bankLock.newCondition();
   }

   /**
    * Transfers money from one account to another.
    * @param from the account to transfer from
    * @param to the account to transfer to
    * @param amount the amount to transfer
    */
   public void transfer(int from, int to, double amount) throws InterruptedException
   {
      bankLock.lock();//调用锁
      try
      {
         while (accounts[from] < amount)//当 账户总余额大于转出金额
            sufficientFunds.await();//调用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();//调用signalAll解除等待集中的所有线程的阻塞状态
      }
      finally
      {
         bankLock.unlock();//解锁
      }
   }

   /**
    * Gets the sum of all account balances.
    * @return the total balance
    */
   public double getTotalBalance()
   {
      bankLock.lock();
      try
      {
         double sum = 0;

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

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

   /**
    * Gets the number of accounts in the bank.
    * @return the number of accounts
    */
   public int size()
   {
      return accounts.length;
   }
}

 

package synch;

/**
 * This program shows how multiple threads can safely access a data structure.
 * @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()));//调用sleep,随机
               }
            }
            catch (InterruptedException e)//抛出异常
            {
            }            
         };
         Thread t = new Thread(r);//建立Thread类对象
         t.start();//启动线程
      }
   }
}

 实验输出结果截图为:

 

 测试程序2:

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

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

 程序源代码为:

 

package synch2;

import java.util.*;

/**
 * A bank with a number of bank accounts that uses synchronization primitives.
 * @version 1.30 2004-08-01
 * @author Cay Horstmann
 */
public class Bank
{
   private final double[] accounts;

   /**
    * Constructs the bank.
    * @param n the number of accounts
    * @param initialBalance the initial balance for each account
    */
   public Bank(int n, double initialBalance)
   {
      accounts = new double[n];
      Arrays.fill(accounts, initialBalance);
   }

   /**
    * Transfers money from one account to another.
    * @param from the account to transfer from
    * @param to the account to transfer to
    * @param amount the amount to transfer
    */
   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();//解除调用wait方法的线程的阻塞状态
   }

   /**
    * Gets the sum of all account balances.
    * @return the total balance
    */
   public synchronized double getTotalBalance()
   {
      double sum = 0;

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

      return sum;
   }

   /**
    * Gets the number of accounts in the bank.
    * @return the number of accounts
    */
   public int size()
   {
      return accounts.length;
   }
}

 

package synch2;

/**
 * This program shows how multiple threads can safely access a data structure,
 * using synchronized methods.
 * @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();//启动线程
      }
   }
}

 实验输出结果截图为:

 

 测试程序3

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

尝试解决下列程序中的问题

 

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 wf;
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 wf;
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张票

实验代码为:

package wf;

import java.nio.charset.MalformedInputException;

public class w {
    public  static void main(String[] args)
    {
        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 t=1;
    boolean flag=true;
     
    public void run() {
        
        while(flag) {
            try {
                
                 Thread.sleep(500);
                  }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }            
            
            
            
             synchronized  (this) {
            
            if(t<=10){
                System.out.println(Thread.currentThread().getName()+"窗口售:第"+t+"张票");
                t++;
            }
            if(t>10){
              flag=false;    
            }
            
        }
        
    }
}
}

实验输出结果截图为:

 

 

 第三部分 实验总结

     这周是我们上的最后一节java课,也预示着一学期的Java课结束了,但是课堂上的我们还是没有丝毫松懈。这周我们主要还是学习了有关线程方面更加重要的知识。有关线程并发执行中可能出现的各种问题,比如当有两个或者两个以上的线程共享某个对象,每个线程都调用了改变该对象类状态的方法时会初心什么情况。就这个问题,老师给我们展开了详细的解说。正如老师说的“这些问题都是我们在使用计算机的过程中真实会发生的情况”所以掌握这些知识是极其重要的。对于我个人而言,我觉得这节课掌握的相对较好的知识点是有关线程的状态这个方面,而且在这些知识点上老师通过图表讲解的非常详细。我以后也会继续好好学习Java相关知识。

posted @ 2019-12-23 20:56  计师-王方  阅读(124)  评论(1编辑  收藏  举报