16穷吉201771010119
实验十六 线程技术
理论知识:
程序是一段静态的代码,它是应用程序执行的蓝
本。
‐进程是程序的一次动态执行,它对应了从代码加
载、执行至执行完毕的一个完整过程。
多线程是进程执行过程中产生的多条执行线索。
‐线程是比进程执行更小的单位。
‐线程不能独立存在,必须存在于进程中,同一进
程的各线程间共享进程空间的数据。
‐每个线程有它自身的产生、存在和消亡的过程,
是一个动态的概念。
‐多线程意味着一个程序的多行语句可以看上去几
乎在同一时间内同时运行。
(2)Java中实现多线程的途径有两种:
‐创建Thread类的子类
<1>用Thread类的子类创建线程
首先需从Thread类派生出一个子类,在该子类中
重写run()方法。
例:
class hand extends Thread
{
public void run()
{……}
}
<2>然后用创建该子类的对象
Lefthand left=new Lefthand();
Righthand right=new Righthand();
<3>最后用start()方法启动线程
left.start();
right.start();
用Thread类的子类创建多线程的关键性操作
–定义Thread类的子类并实现用户线程操作,即
run()方法的实现。
实验目的与要求
(1) 掌握线程概念;
(2) 掌握线程创建的两种技术;
(3) 理解和掌握线程的优先级属性及调度方法;
(4) 掌握线程同步的概念及实现技术;
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(); } } |
用 Runnable接口改造后的程序
package ceshi;
class Lefthand implements Runnable {
public void run()
{
for(int i=0;i<=5;i++)
{ System.out.println("You are Students!");
try{ Thread. sleep(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); }
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();
Runnable Lefthand=new Lefthand();
Thread t=new Thread(Lefthand);
t.start();
Runnable Righthand=new Righthand();
Thread t1=new Thread(Righthand);
t1.start();
}
}
测试程序2:
l 在Elipse环境下调试教材625页程序14-1、14-2 、14-3,结合程序运行结果理解程序;
在Elipse环境下调试教材631页程序14-4,结合程序运行结果理解程序;
l 对比两个程序,理解线程的概念和用途;
l 掌握线程创建的两种技术
package bounceThread; import java.awt.geom.*; /** A ball that moves and bounces off the edges of a rectangle * @version 1.33 2007-05-17 * @author Cay Horstmann */ public class Ball { private static final int XSIZE = 15; private static final int YSIZE = 15; private double x = 0; private double y = 0; private double dx = 1; private double dy = 1; /** Moves the ball to the next position, reversing direction if it hits one of the edges */ //判断球的边界所处状态的四个条件 public void move(Rectangle2D bounds) { x += dx; y += dy; if (x < bounds.getMinX()) { x = bounds.getMinX(); dx = -dx; } if (x + XSIZE >= bounds.getMaxX()) { x = bounds.getMaxX() - XSIZE; dx = -dx; } if (y < bounds.getMinY()) { y = bounds.getMinY(); dy = -dy; } if (y + YSIZE >= bounds.getMaxY()) { y = bounds.getMaxY() - YSIZE; dy = -dy; } } /** Gets the shape of the ball at its current position. */ public Ellipse2D getShape() { return new Ellipse2D.Double(x, y, XSIZE, YSIZE); } }
package bounceThread; import java.awt.*; import java.util.*; import javax.swing.*; /** * The component that draws the balls. * @version 1.34 2012-01-26 * @author Cay Horstmann */ public class BallComponent extends JComponent { private static final int DEFAULT_WIDTH = 450; private static final int DEFAULT_HEIGHT = 350; private java.util.List<Ball> balls = new ArrayList<>(); /** * Add a ball to the panel. * @param b the ball to add */ public void add(Ball b) { balls.add(b); } public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; for (Ball b : balls) { g2.fill(b.getShape()); } } public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); } }
package bounce; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * Shows an animated bouncing ball. * @version 1.34 2015-06-21 * @author Cay Horstmann */ public class Bounce { public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame frame = new BounceFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); } } /** * The frame with ball component and buttons. */ class BounceFrame extends JFrame { private BallComponent comp; public static final int STEPS = 1000; public static final int DELAY = 3; /** * Constructs the frame with the component for showing the bouncing ball and * Start and Close buttons */ public BounceFrame() { setTitle("Bounce"); 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);//将buttonPanel组件整体放在南端 pack(); } /** * Adds a button to a container. * @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); } /** * Adds a bouncing ball to the panel and makes it bounce 1,000 times. */ public void addBall() { try { Ball ball = new Ball(); comp.add(ball); for (int i = 1; i <= STEPS; i++) { ball.move(comp.getBounds());//调用move方法 comp.paint(comp.getGraphics()); Thread.sleep(DELAY);//调用线程的sleep方法 } } catch (InterruptedException e) { } } }
14-4
package bounceThread; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * Shows animated bouncing balls. * @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); }); } } /** * The frame with panel and buttons. */ class BounceFrame extends JFrame { private BallComponent comp; public static final int STEPS = 1000; public static final int DELAY = 5; /** * Constructs the frame with the component for showing the bouncing ball and * Start and Close buttons */ 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(); } /** * Adds a button to a container. * @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); } /** * Adds a bouncing ball to the canvas and starts a thread to make it bounce */ public void addBall() { Ball ball = new Ball(); comp.add(ball); //引用实现了Runnable的方法 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);//用Runnable创建一个Thread对象 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()+"已计算完毕!"); } } |
测试程序4
l 教材642页程序模拟一个有若干账户的银行,随机地生成在这些账户之间转移钱款的交易。每一个账户有一个线程。在每一笔交易中,会从线程所服务的账户中随机转移一定数目的钱款到另一个随机账户。
l 在Elipse环境下调试教材642页程序14-5、14-6,结合程序运行结果理解程序;
代码:
package unsynch; import java.util.*; /** * A bank with a number of bank accounts. * * @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 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()); } /** * Gets the sum of all account balances. * * @return the total balance */ public 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 unsynch; /** * This program shows data corruption when multiple threads access a data * structure. * * @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);//分配新的 Thread 对象 t.start();//使该线程开始执行 } } }
综合编程练习
编程练习1
- 1. 设计一个用户信息采集程序,要求如下:
(1) 用户信息输入界面如下图所示:
(2) 用户点击提交按钮时,用户输入信息显示控制台界面;
(3) 用户点击重置按钮后,清空用户已输入信息;
(4) 点击窗口关闭,程序退出。
代码:
package 银行;
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(700, 45));
// 给面板的布局设置为网格布局 一行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("学历:");
comboBox = new JComboBox();
comboBox.addItem("初中");
comboBox.addItem("高中");
comboBox.addItem("本科");
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("地址:");
fieldadress = new JTextField();
fieldadress.setPreferredSize(new Dimension(150, 50));
JLabel study = new JLabel("民族:");
JPanel selectBox = new JPanel();
selectBox.setBorder(BorderFactory.createTitledBorder(""));
selectBox.setLayout(new GridLayout(3, 1));
//创建具有指定行数和列数的网格布局。给布局中的所有组件分配相等的大小
sing = new JCheckBox("藏族");
dance = new JCheckBox("汉族");
draw = new JCheckBox("蒙古族");
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("性别:");
JPanel selectBox = new JPanel();
selectBox.setBorder(BorderFactory.createTitledBorder(""));
selectBox.setLayout(new GridLayout(2, 1));
bg = new ButtonGroup();
nan = new JRadioButton("男");
nv = new JRadioButton("女");
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("提交");
JButton reset = new JButton("重置");
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+="藏族 ";
}
if (dance.isSelected()) {
hobbystring+="汉族 ";
}
if (draw.isSelected()) {
hobbystring+="蒙古族 ";
}
System.out.println(address);
if (nan.isSelected()) {
System.out.println("男");
}
if (nv.isSelected()) {
System.out.println("女");
}
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();
}
}
package 银行;
import java.awt.EventQueue;
import javax.swing.JFrame;
public class Mian {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
DemoJFrame page = new DemoJFrame();
});
}
}
package 银行;
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);
}
}
结果图:
2.创建两个线程,每个线程按顺序输出5次“你好”,每个“你好”要标明来自哪个线程及其顺序号。
package 线程;
class xiancheng1 extends Thread {
public void run()
{
for(int i=1;i<=5;i++)
{ System.out.println( i+ "你好~"+"(线程1)");
try{ sleep(500); }
catch(InterruptedException e)//异常捕获
{ System.out.println("Lefthand error.");}
}
}
}
class xiancheng2 extends Thread {
public void run()
{
for(int i=1;i<=5;i++)
{ System.out.println( i+ "你好~"+"(线程2)");
try{ sleep(500); }
catch(InterruptedException e)//异常捕获
{ System.out.println("Lefthand error.");}
}
}
}
public class xiancheng
{
//属性
static xiancheng1 xian1;
static xiancheng2 xian2;
public static void main(String[] args)
{ xian1=new xiancheng1();
xian2=new xiancheng2();
//用start()方法启动线程
xian1.start();
xian2.start();
}
}
3. 完善实验十五 GUI综合编程练习程序。
实验总结:这一章节我们学习了关于线程的知识点,掌握线程的概念和定义并进行运用。