刘志梅201771010115.《面向对象程序设计(java)》第十六周学习总结
实验十六 线程技术
实验时间 2017-12-8
1、实验目的与要求
(1)当线程的run方法执行方法体中最后一条语句后,并经由执行return语句返回时,或者出现了在方法中没有捕获的异常时,线程将终止。
当对一个线程调用interrupt方法时,线程的中断状态将被置位。
每个线程都应该检查boolean标志,以判断线程是否被中断。
如果线程被阻塞,无法检测中断状态。
当在一个被阻塞的线程上调用interrupt方法时,阻塞调用将会被interrupt Exception异常中断。
(2) 线程的6种状态:New(新创建)、Runnable(可运行)、Bloced(被阻塞)、Waiting(等待)、Timed waiting(计时等待)、Terminated(被终止)。
当一个线程处于新创建状态时,程序还没有开始运行程序中的代码。
一旦调用start方法,线程处于runnable状态。
一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供运行的时间。
在具有多个处理器的机器上,每一个处理器运行一个线程,可以有多个线程并行运行。
当一个线程试图获取一个内部的对象锁,而该锁被其他线程持有,则该线程进入阻塞状态;当一个线程等待另一个线程通知调度器一个条件时,它自己进入等待状态;有几个方法有一个超时参数,调用它们线程进入计时等待状态。
线程被终止的原因有如下两个:因为run方法正常退出而自然死亡;因为一个没有捕获的异常终止了run方法而意外死亡。
(3) 线程属性包括:线程优先级、守护线程、线程组以及处理未捕获异常的处理器。
在JAVA程序设计语言中,每一个线程有一个优先级;默认情况下,一个线程继承它的父线程的优先级,可以用setPriority方法提高或降低任何一个线程的优先级。
优先级可以设置为在MIN_PRIORITY(在Thread的类中定义为1)与MAX_PRIORITY(定义为10)之间的任何值;NORM_PRIORITY被定义为5。
可以通过调用t.setDaemon(true);将线程转换为守护线程。
线程的run方法不能抛出任何受查异常,但是,非受查异常会导致线程终止。
线程组是一个可以统一管理的线程集合。
(4) 在大多数实际的多线程应用中,两个或两个以上的线程需要共享对同一数据的存取;如果两个线程存取相同的对象,并且每一个线程都调用了一个修改该对象状态的方法,此时这种情况称为竞争条件。
(5) 每一个Back对象都有自己的ReentrantLock对象,如果两个线程试图访问同一个Back对象,那么锁以串行方式提供服务。
锁和条件的关键之处:锁用来保护代码片段,任何时刻只能有一个线程执行被保护的代码;锁可以管理任何试图进入被保护代码段的线程;锁可以拥有一个或多个相关的条件对象;每个条件对象管理那些已经进入被保护的代码段但还不能运行的线程。
内部锁和条件存在的一些局限:不能中断一个正在试图获得锁的线程;试图获得锁时不能设定超时;每个锁仅有单一的条件,这可能是不够的。
(6)监视器的特性:监视器是只包含私有域的类;每一个监视器类的对象都有一个相关的锁;使用该锁对所有的方法进行加锁;该锁可以有任意多个相关条件。
在下述三个方面java对象不同于监视器,从而使得线程的安全性下降:域不要求必须是private;方法不要求必须是synchronized;内部锁对客户是可用的。
Volatile关键字为实例域的同步访问提供了一种免锁机制。
除非使用锁或Volatile修饰符,否则无法从多个线程安全的读取一个域。
假设对共享变量除了赋值之外并不完成其它操作,那么可以将这些共享变量声明为Volatile。
如果另一个线程也在更新largest,就可能阻止这个线程更新。
有可能因为每一个线程要等待更多的钱款存入而导致所有线程都阻塞,这样的状态被称为死锁。
(7)Swing不是线程安全的。
将线程与Swing一起使用时,必须遵守的两个简单原则:如果一个动作需要花费很长时间,在一个独立的工作器线程中做这件事不要在事件分配线程中做;除了事件分配线程,不要在任何线程中接触Swing组件。
2、实验内容和步骤
实验1:测试程序并进行代码注释。
测试程序1:
l 在elipse IDE中调试运行ThreadTest,结合程序运行结果理解程序;
l 掌握线程概念;
l 掌握用Thread的扩展类实现线程的方法;
l 利用Runnable接口改造程序,掌握用Runnable接口创建线程的方法。
class Lefthand extends Thread { public void run() { for(int i=0;i<=5;i++) { System.out.println("You are Students!"); try{ sleep(500); } catch(InterruptedException e) { System.out.println("Lefthand error.");} } } } class Righthand extends Thread { public void run() { for(int i=0;i<=5;i++) { System.out.println("I am a Teacher!"); try{ sleep(300); } catch(InterruptedException e) { System.out.println("Righthand error.");} } } } public class ThreadTest { static Lefthand left; static Righthand right; public static void main(String[] args) { left=new Lefthand(); right=new Righthand(); left.start(); right.start(); } } |
class Lefthand implements Runnable { public void run() { for (int i = 0; i <= 5; i++) { System.out.println("You are Students!"); try { Thread.sleep(500); } // 休眠时间为500毫秒 catch (InterruptedException e)// 阻塞状态 { System.out.println("Lefthand error."); } } } } class Righthand implements Runnable { public void run() { for (int i = 0; i <= 5; i++) { System.out.println("I am a Teacher!"); try { Thread.sleep(300); } // 休眠时间为300毫秒 catch (InterruptedException e) { System.out.println("Righthand error."); } // } } } public class thresdtest { private static Runnable Right; public static void main(String[] args) { Righthand righthand = new Righthand(); Thread right = new Thread(righthand); right.start(); Lefthand lefthand=new Lefthand(); Thread left = new Thread(lefthand); left.start(); } }
修改后:
class Lefthand implements Runnable { public void run() { for (int i = 0; i <= 5; i++) { System.out.println("You are Students!"); try { Thread.sleep(500); } // 休眠时间为500毫秒 catch (InterruptedException e)// 阻塞状态 { System.out.println("Lefthand error."); } } } } class Righthand implements Runnable { public void run() { for (int i = 0; i <= 5; i++) { System.out.println("I am a Teacher!"); try { Thread.sleep(300); } // 休眠时间为300毫秒 catch (InterruptedException e) { System.out.println("Righthand error."); } // } } } public class thresdtest { private static Runnable Right; public static void main(String[] args) { Righthand righthand = new Righthand(); Lefthand lefthand=new Lefthand(); Thread right = new Thread(righthand); Thread left = new Thread(lefthand); right.start(); left.start(); } }
测试程序2:
l 在Elipse环境下调试教材625页程序14-1、14-2 、14-3,结合程序运行结果理解程序;
l 在Elipse环境下调试教材631页程序14-4,结合程序运行结果理解程序;
l 对比两个程序,理解线程的概念和用途;
l 掌握线程创建的两种技术。
package bounceThread; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * 显示动画弹跳球。 * @version 1.34 2015-06-21 * @author Cay Horstmann */ public class BounceThread { public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame frame = new BounceFrame(); frame.setTitle("BounceThread"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); } } /** * 带有面板和按钮的框架。 */ class BounceFrame extends JFrame { private BallComponent comp; public static final int STEPS = 1000; public static final int DELAY = 5; /** * 使用组件构造框架,以显示弹跳球和开始和关闭按钮 */ public BounceFrame() { comp = new BallComponent(); add(comp, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); addButton(buttonPanel, "Start", event -> addBall()); addButton(buttonPanel, "Close", event -> System.exit(0)); add(buttonPanel, BorderLayout.SOUTH); pack(); } /** * 将按钮添加到容器中。 * @param c the container * @param title the button title * @param listener the action listener for the button */ public void addButton(Container c, String title, ActionListener listener) { JButton button = new JButton(title); c.add(button); button.addActionListener(listener); } /** * 在画布上添加一个弹跳球,并启动一根线使其弹跳 */ public void addBall() { Ball ball = new Ball(); comp.add(ball); Runnable r = () -> { try { for (int i = 1; i <= STEPS; i++) { ball.move(comp.getBounds()); comp.repaint(); Thread.sleep(DELAY); } } catch (InterruptedException e) { } }; Thread t = new Thread(r); t.start(); } }
测试程序3:分析以下程序运行结果并理解程序。
class Race extends Thread { public static void main(String args[]) { Race[] runner=new Race[4]; for(int i=0;i<4;i++) runner[i]=new Race( ); for(int i=0;i<4;i++) runner[i].start( ); runner[1].setPriority(MIN_PRIORITY); runner[3].setPriority(MAX_PRIORITY);} public void run( ) { for(int i=0; i<1000000; i++); System.out.println(getName()+"线程的优先级是"+getPriority()+"已计算完毕!"); } } |
class Race extends Thread {//Race继承于Thread public static void main(String args[]) { Race[] runner=new Race[4]; for(int i=0;i<4;i++) runner[i]=new Race( ); for(int i=0;i<4;i++) runner[i].start( ); runner[1].setPriority(MIN_PRIORITY); runner[3].setPriority(MAX_PRIORITY); } public void run( ) { for(int i=0; i<1000000; i++);//(;)延时作用(使后面的10与11行代码执行完) System.out.println(getName()+"线程的优先级是"+getPriority()+"已计算完毕!"); } }
测试程序4
l 教材642页程序模拟一个有若干账户的银行,随机地生成在这些账户之间转移钱款的交易。每一个账户有一个线程。在每一笔交易中,会从线程所服务的账户中随机转移一定数目的钱款到另一个随机账户。
l 在Elipse环境下调试教材642页程序14-5、14-6,结合程序运行结果理解程序;
package unsynch; import java.util.*; /** * 有许多银行账户的银行。 * @version 1.30 2004-08-01 * @author Cay Horstmann */ public class Bank { private final double[] accounts; /** *构建了银行。 * @param n账户数量 * @param 每个帐户的初始余额 */ public Bank(int n, double initialBalance) { accounts = new double[n]; Arrays.fill(accounts, initialBalance); } /** * 把钱从一个账户转到另一个账户。 * @param 从账户转出 * @param 到账转到 * @param 转帐金额 */ public void transfer(int from, int to, double amount) { if (accounts[from] < amount) return; 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()); } /** * 获取所有帐户余额的总和。 * @return 总平衡 */ public double getTotalBalance() { double sum = 0; for (double a : accounts) sum += a; return sum; } /** * 获取银行中的帐户编号。 * @return 账户数量 */ public int size() { return accounts.length; } } package unsynch; /** * 当多个线程访问一个数据结构时,这个程序显示数据损坏。 * @version 1.31 2015-06-21 * @author Cay Horstmann */ public class UnsynchBankTest { 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
1. 设计一个用户信息采集程序,要求如下:
(1) 用户信息输入界面如下图所示:
(2) 用户点击提交按钮时,用户输入信息显示控制台界面;
(3) 用户点击重置按钮后,清空用户已输入信息;
(4) 点击窗口关闭,程序退出。
2.创建两个线程,每个线程按顺序输出5次“你好”,每个“你好”要标明来自哪个线程及其顺序号。
3. 完善实验十五 GUI综合编程练习程序。
import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.LayoutManager; import java.awt.Panel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Timer; import java.util.TimerTask; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.ButtonModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JTextField; public class DemoJFrame extends JFrame { private JPanel jPanel1; private JPanel jPanel2; private JPanel jPanel3; private JPanel jPanel4; private JTextField fieldname; private JComboBox comboBox; private JTextField fieldadress; private ButtonGroup bg; private JRadioButton nan; private JRadioButton nv; private JCheckBox sing; private JCheckBox dance; private JCheckBox draw; public DemoJFrame() { // 设置窗口大小 this.setSize(800, 400); // 设置可见性 this.setVisible(true); // 设置标题 this.setTitle("编程练习一"); // 设置关闭操作 this.setDefaultCloseOperation(EXIT_ON_CLOSE); // 设置窗口居中 WinCenter.center(this); // 创建四个面板对象 jPanel1 = new JPanel(); setJPanel1(jPanel1); jPanel2 = new JPanel(); setJPanel2(jPanel2); jPanel3 = new JPanel(); setJPanel3(jPanel3); jPanel4 = new JPanel(); setJPanel4(jPanel4); // 设置容器的为流布局 FlowLayout flowLayout = new FlowLayout(); this.setLayout(flowLayout); // 将四个面板添加到容器中 this.add(jPanel1); this.add(jPanel2); this.add(jPanel3); this.add(jPanel4); } /* * 设置面一 */ private void setJPanel1(JPanel jPanel) { // TODO 自动生成的方法存根 jPanel.setPreferredSize(new Dimension(750, 50)); // 给面板的布局设置为网格布局 一行4列 jPanel.setLayout(new GridLayout(1, 4)); JLabel name = new JLabel("姓名"); name.setSize(100, 50); fieldname = new JTextField(""); fieldname.setSize(80, 20); JLabel study = new JLabel("edcational background"); comboBox = new JComboBox(); comboBox.addItem("junior high school"); comboBox.addItem("senior high school"); comboBox.addItem("regular college course"); jPanel.add(name); jPanel.add(fieldname); jPanel.add(study); jPanel.add(comboBox); } /* * 设置面板二 */ private void setJPanel2(JPanel jPanel) { // TODO 自动生成的方法存根 jPanel.setPreferredSize(new Dimension(700, 50)); // 给面板的布局设置为网格布局 一行4列 jPanel.setLayout(new GridLayout(1, 4)); JLabel name = new JLabel("address"); fieldadress = new JTextField(); fieldadress.setPreferredSize(new Dimension(150, 50)); JLabel study = new JLabel("hobby"); JPanel selectBox = new JPanel(); selectBox.setBorder(BorderFactory.createTitledBorder("")); selectBox.setLayout(new GridLayout(3, 1)); sing = new JCheckBox("sing"); dance = new JCheckBox("dance"); draw = new JCheckBox("draw"); selectBox.add(sing); selectBox.add(dance); selectBox.add(draw); jPanel.add(name); jPanel.add(fieldadress); jPanel.add(study); jPanel.add(selectBox); } /* * 设置面板三 */ private void setJPanel3(JPanel jPanel) { // TODO 自动生成的方法存根 jPanel.setPreferredSize(new Dimension(700, 150)); FlowLayout flowLayout = new FlowLayout(FlowLayout.LEFT); jPanel.setLayout(flowLayout); JLabel sex = new JLabel("sex"); JPanel selectBox = new JPanel(); selectBox.setBorder(BorderFactory.createTitledBorder("")); selectBox.setLayout(new GridLayout(2, 1)); bg = new ButtonGroup(); nan = new JRadioButton("man"); nv = new JRadioButton("woman"); bg.add(nan); bg.add(nv); selectBox.add(nan); selectBox.add(nv); jPanel.add(sex); jPanel.add(selectBox); } /* * 设置面板四 */ private void setJPanel4(JPanel jPanel) { // TODO 自动生成的方法存根 jPanel.setPreferredSize(new Dimension(700, 150)); FlowLayout flowLayout = new FlowLayout(FlowLayout.CENTER, 50, 10); jPanel.setLayout(flowLayout); jPanel.setLayout(flowLayout); JButton sublite = new JButton("submit"); JButton reset = new JButton("resetting"); sublite.addActionListener((e) -> valiData()); reset.addActionListener((e) -> Reset()); jPanel.add(sublite); jPanel.add(reset); } /* * 提交数据 */ private void valiData() { // TODO 自动生成的方法存根 // 拿到数据 String name = fieldname.getText().toString().trim(); String xueli = comboBox.getSelectedItem().toString().trim(); String address = fieldadress.getText().toString().trim(); System.out.println(name); System.out.println(xueli); String hobbystring=""; if (sing.isSelected()) { hobbystring+="sing "; } if (dance.isSelected()) { hobbystring+="dance "; } if (draw.isSelected()) { hobbystring+="draw "; } System.out.println(address); if (nan.isSelected()) { System.out.println("man"); } if (nv.isSelected()) { System.out.println("woman"); } System.out.println(hobbystring); } /* * 重置 */ private void Reset() { // TODO 自动生成的方法存根 fieldadress.setText(null); fieldname.setText(null); comboBox.setSelectedIndex(0); sing.setSelected(false); dance.setSelected(false); draw.setSelected(false); bg.clearSelection(); } }
import java.awt.EventQueue; import javax.swing.JFrame; public class Mian { public static void main(String[] args) { EventQueue.invokeLater(() -> { DemoJFrame page = new DemoJFrame(); }); } }
import java.awt.Dimension; import java.awt.Toolkit; import java.awt.Window; public class WinCenter { public static void center(Window win){ Toolkit tkit = Toolkit.getDefaultToolkit(); Dimension sSize = tkit.getScreenSize(); Dimension wSize = win.getSize(); if(wSize.height > sSize.height){ wSize.height = sSize.height; } if(wSize.width > sSize.width){ wSize.width = sSize.width; } win.setLocation((sSize.width - wSize.width)/ 2, (sSize.height - wSize.height)/ 2); } }
package hello; public class hello { static Lefthand left; static Righthand right; public static void main(String[] args) { left = new Lefthand();//创建左手线程 right = new Righthand();//创建右手线程 left.start(); right.start();//启动线程 } } class Lefthand extends Thread { public void run() { for (int i = 1; i <= 5; i++) { System.out.println(i+":lefthand Hello");//输出形式 try { sleep(300);//休眠300毫秒 } catch (InterruptedException e) { System.out.println("Lefthand error."); } } } } class Righthand extends Thread { public void run() { for (int i = 1; i <= 5; i++) { System.out.println(i+":righthand Hello"); try { sleep(300); } catch (InterruptedException e) { System.out.println("Righthand error."); } } } }
实验总结:通过本周实验学习了什么是线程,中断线程的概念,线程的状态以及多线程;用Thread类的子类创建线程,用start()方法启动线程,用Runnable()接口实现线程等等;最后的实验编程练习通过学长的耐心讲解掌握了一些。