201871010119-帖佼佼《面向对象程序设计(java)》第十七周学习总结
博文正文开头格式:(2分)
项目 |
内容 |
这个作业属于哪个课程 |
https://www.cnblogs.com/nwnu-daizh/ |
这个作业的要求在哪里 |
https://www.cnblogs.com/nwnu-daizh/p/11435127.html |
作业学习目标 |
(1) 掌握菜单组件用途及常用API; (2) 掌握对话框组件用途及常用API; (3) 学习设计简单应用程序的GUI。 |
随笔博文正文内容包括:
第一部分:总结线程同步技术(10分)
19、线程的同步:
多线程并发运行不确定性问题解决方案:
引入线程同步机制;
在Java中线程同步方法有两种:
(1)JavaSE5.0中引入ReentrantLock类;
20、有关锁对象和条件对象的关键要点:
锁用来保护代码片段,保证任何时刻只能有一个线程被保护执行的代码;
锁管理试图进入被保护的代码段的线程;
锁可拥有一个或多个相关的条件对象;
每个条件对象管理那些已经进入被保护得到代码段但还不能运行的线程;
21、在临界区中使用条件对象的await()、signal()、signalAll()方法实现线程之间的交互:
一个线程在临界区时,可能根据问题的需要,必须使用锁对象的await()方法使本线程等待,暂时让出CPU的使用权,并允许其他线程使用这个同步方法;
线程若退出临界区,应用signal()方法随机的选择一个线程解除其阻塞状态;
线程若退出临界区,执行notifyAll()方法通知所有由于等待该临界区的线程结束等待。
(2)在共享内存中的类方法前加synchronized修饰符;
synchronized关键字作用:
某个类内方法用synchronized修饰符后,该方法被称为同步线程;
只要某个线程正在访问同步方法,其他线程欲要访问同步方法就会被阻塞,直至线程从同步方法返回前唤醒被阻塞线程,其他线程方法可能进入同步方法;
第二部分:实验部分
实验1:测试程序1(5分)
l 在Elipse环境下调试教材651页程序14-7,结合程序运行结果理解程序;
l 掌握利用锁对象和条件对象实现的多线程同步技术。
实验代码如下:
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) //Bank构造器,两个入口参数 { 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(); //把该线程放在条件的等待集中,当前线程被阻塞,并且放弃了锁 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(); //释放这个锁 } } /** * 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 = () -> { //Runnable接口实现创建线程 try { while (true) { int toAccount = (int) (bank.size() * Math.random()); //随机函数产生 double amount = MAX_AMOUNT * Math.random(); //随机函数产生MAX_AMOUNT个随机数 bank.transfer(fromAccount, toAccount, amount); //调用transfer方法在同行之间进行转账 Thread.sleep((int) (DELAY * Math.random())); //随机睡眠时间DELAY } } catch (InterruptedException e) { } }; Thread t = new Thread(r); //创建一个线程 t.start(); //该线程开始工作 } } }
运行结果如下:
实验1:测试程序2(5分)
l 在Elipse环境下调试教材655页程序14-8,结合程序运行结果理解程序;
l 掌握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) //Bank构造器 { 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(); //调用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(); //唤醒等待使用该资源的其他线程 } /** * 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() //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 = () -> { //Runnable接口创建线程 try { while (true) { int toAccount = (int) (bank.size() * Math.random()); //随机函数 double amount = MAX_AMOUNT * Math.random(); //产生 1000个随机数 bank.transfer(fromAccount, toAccount, amount); //transfer方法在同行之间进行转账 Thread.sleep((int) (DELAY * Math.random())); //随机产生0-10毫秒之间的一个数,调用sleep方法睡眠。 } } catch (InterruptedException e) { } }; Thread t = new Thread(r); //新建一个线程 t.start(); //调用start方法开启线程 } } }
运行结果如下:
实验1:测试程序3(5分)
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(); } }
运行结果如下:
实验2:结对编程练习包含以下4部分(10分)
利用多线程及同步方法,编写一个程序模拟火车票售票系统,共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张票
1) 程序设计思路简述;
在主函数当中创建三个线程,写一个线程类,是实现Runnable接口的类,调用synchronized关键字实现线程的同步。
2) 符合编程规范的程序代码;
3) 程序运行功能界面截图;
实验代码如下:
class thread implements Runnable { int ticket = 1; boolean flag = true; @Override public void 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++; } if (ticket > 10) { flag = false; } } } } } public class Demo { public static void main(String[] args) { thread mythread = new thread(); Thread ticket1 = new Thread(mythread); Thread ticket2 = new Thread(mythread); Thread ticket3 = new Thread(mythread); ticket1.start(); ticket2.start(); ticket3.start(); } }
运行结果如下:
实验总结:(5分)
本周学习了线程的知识,这次作业主要是关于同步线程的知识。上课通过老师的讲解代码以及运行结果的分析,对同步线程又了一定的理解,掌握了线程同步的两种方法。(1)引入ReentrantLock类;(2)在共享内存中的类方法前加synchronized修饰符;
synchronized关键字作用: 某个类内方法用synchronized修饰符后,该方法被称为同步线程; 只要某个线程正在访问同步方法,其他线程欲要访问同步方法就会被阻塞,直至线程从同步方法返回前唤醒被阻塞线程,其他线程方法可能进入同步方法.编程练习是对同步线程的应用。