实验十六 线程技术
实验时间 2017-12-8
1、实验目的与要求
(1) 掌握线程概念;
(2) 掌握线程创建的两种技术;
(3) 理解和掌握线程的优先级属性及调度方法;
(4) 掌握线程同步的概念及实现技术;
.Java实现多线程的两个方法
‐创建Thread类的子类
‐在程序中定义实现Runnable接口的类
用Thread类的子类创建线程
a:首先需从Thread类派生出一个子类,在该子类中 重写run()方法。
b:然后用创建该子类的对象
c:最后用start()方法启动线程 left.start(); right.start();
用Thread类的子类创建多线程的关键性操作
a:定义Thread类的子类并实现用户线程操作,即 run()方法的实现。 –在适当的时候启动线程。
b:由于Java只支持单重继承,用这种方法定义的类不 可再继承其他父类。
2.用Runnable接口实现多线程
a:首先设计一个实现Runnable接口的类;
b:然后在类中根据需要重写run方法;
c:再创建该类对象,以此对象为参数建立Thread 类的对象;
d调用Thread类对象的start方法启动线程,将 CPU执行权转交到run方法。
线程有如下7种状态:New (新建);Runnable (可运行);Running(运行) ;Blocked (被阻塞) ;Waiting (等待) ;Timed waiting (计时等待) ; Terminated (被终止)。
1. new(新建):线程对象刚刚创建,还没有启动,此时线程还处于不可运行状态。例如: Thread thread=new Thread(r); 此时线程thread处于新建状态,有了相应的内存空间以及其它资源。
2. runnable(可运行状态):此时线程已经启动,处于线程的run()方法之中。此时的线程可能运行,也可能不运行,只要 CPU一空闲,马上就会运行。调用线程的start()方法可使线程处于“可运行”状态。例如: thread.start();
3.blocked (被阻塞):一个正在执行的线程因特殊原因,被暂停执行,进入阻塞状态。阻塞时线程不能进入队列排队,必须等到引起阻塞的原因消除,才可重新进入排队队列。引起阻塞的原因很多,不同原因要用不同的方法解除。sleep(),wait()是两个常用引起线程阻塞的方法。
线程阻塞的三种情况:等待阻塞:通过调用线程的wait()方法,让线程等待某工作的完成。同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻 塞状态。 其他阻塞:通过调用线程的sleep()或join() 或发出了I/O请求时,线程会进入到阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
4.Terminated (被终止) :线程被终止的原因有二:一是run()方法中最后一个语句执行完毕而自 然死亡。二是因为一个没有捕获的异常终止了run方法而意外死亡。可以调用线程的 stop 方法杀死一个线程(thread.stop();),但是,stop方法已过时,不要在自己的代码中调用它。
java 中的线程优先级的范围是1~10,默认的优先级是5。“高优先级线程”会优先于“低优先级线程”执行。
java 中有两种线程:用户线程和守护线程。可以通过isDaemon()方法来区别它们:如果返回false,则说明该线程是“用户线程”;否则就是“守护线程”。用户线程一般用于执行用户级任务,而守护线程也就是“后台线程”,一般用来执行后台任务。需要注意的是:Java虚拟机在“用户线程”都结束后会后退出。
JDK 中关于线程优先级和守护线程的介绍如下:
每个线程都有一个优先级。“高优先级线程”会优先于“低优先级线程”执行。每个线程都可以被标记为一个守护进程或非守护进程。在一些运行的主线程中创建新的子线程时,子线程的优先级被设置为等于“创建它的主线程的优先级”,当且仅当“创建它的主线程是守护线程”时“子线程才会是守护线程”。
当Java虚拟机启动时,通常有一个单一的非守护线程(该线程通过是通过main()方法启动)。JVM会一直运行直到下面的任意一个条件发生,JVM就会终止运行:
(01) 调用了exit()方法,并且exit()有权限被正常执行。
(02) 所有的“非守护线程”都死了(即JVM中仅仅只有“守护线程”)。
每一个线程都被标记为“守护线程”或“用户线程”。当只有守护线程运行时,JVM会自动退出。
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 接口实现:
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(); Lefthand lefthand = new Lefthand(); Righthand righthand = new Righthand(); Thread left=new Thread(lefthand); Thread right=new Thread(righthand); left.start(); right.start(); } }
测试程序2:
l 在Elipse环境下调试教材625页程序14-1、14-2 、14-3,结合程序运行结果理解程序;
l 在Elipse环境下调试教材631页程序14-4,结合程序运行结果理解程序;
l 对比两个程序,理解线程的概念和用途;
l 掌握线程创建的两种技术。
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());// 添加Start按钮 addButton(buttonPanel, "Close", event -> System.exit(0));// 添加Close按钮 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 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()); comp.paint(comp.getGraphics()); Thread.sleep(DELAY);// 在指定的毫秒数内让当前正在执行的线程休眠 } } catch (InterruptedException e) { } } }
package bounce; 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();// 以 double 精度返回 Shape 窗体矩形的最小 X 坐标 dx = -dx; } if (x + XSIZE >= bounds.getMaxX()) { x = bounds.getMaxX() - XSIZE; dx = -dx; } if (y < bounds.getMinY()) { y = bounds.getMinY();// 以 double 精度返回 Shape 窗体矩形的最小 Y 坐标 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);// 根据指定坐标构造和初始化 Ellipse2D }
package bounce; 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();// 以 double 精度返回 Shape 窗体矩形的最小 X 坐标 dx = -dx; } if (x + XSIZE >= bounds.getMaxX()) { x = bounds.getMaxX() - XSIZE; dx = -dx; } if (y < bounds.getMinY()) { y = bounds.getMinY();// 以 double 精度返回 Shape 窗体矩形的最小 Y 坐标 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);// 根据指定坐标构造和初始化 Ellipse2D }
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();//创建具有双缓冲和流布局的新 JPanel。 addButton(buttonPanel, "Start", event -> addBall());//添加Start按钮 addButton(buttonPanel, "Close", event -> System.exit(0));//添加Close按钮 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 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(); } }
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();//以 double 精度返回 Shape 窗体矩形的最小 X 坐标 dx = -dx; } if (x + XSIZE >= bounds.getMaxX()) { x = bounds.getMaxX() - XSIZE; dx = -dx; } if (y < bounds.getMinY()) { y = bounds.getMinY(); //根据指定坐标构造和初始化 Ellipse2D 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);//根据指定坐标构造和初始化 Ellipse2D } }
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);//构造一个 Dimension,并将其初始化为指定宽度和高度。 } }
测试程序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 { 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++);// 执行空语句,用来延时,也可以换成sleep,sleep会释放CPU,会抛出异常 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) 点击窗口关闭,程序退出。
import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.Label; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; 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.JTextArea; import javax.swing.JTextField; public class A extends JFrame{ private JPanel panel1; private JPanel panel2; private JPanel panel3; private JPanel panel4; //private JLabel label; private JComboBox jComboBox; private JTextField jTextField; private JTextArea textArea; private JCheckBox checkBox1; private JCheckBox checkBox2; private JCheckBox checkBox3; private JRadioButton radioButton1; private JRadioButton radioButton2; private JButton button1; private JButton button2; private ButtonGroup buttonGroup; private Object submit; public A(){ //初始化 this.setVisible(true); this.setSize(800, 450); this.setDefaultCloseOperation(EXIT_ON_CLOSE); panel1=new JPanel(); panel2=new JPanel(); panel3=new JPanel(); panel4=new JPanel(); setPanel1(panel1); setPanel2(panel2); setPanel3(panel3); setPanel4(panel4); FlowLayout flowLayout = new FlowLayout(); this.setLayout(flowLayout); this.add(panel1); this.add(panel2); this.add(panel3); this.add(panel4); } private void setPanel1(JPanel panel) { panel.setLayout(new GridLayout(1, 4)); panel.setPreferredSize(new Dimension(700, 45)); JLabel label1= new JLabel("Name:",JLabel.CENTER); jTextField = new JTextField(" "); JLabel jLabel2 = new JLabel("Qualification:",JLabel.CENTER); //jLabel2.setSize(80, 40); jComboBox=new JComboBox(); jComboBox.addItem("本科"); jComboBox.addItem("研究生"); jComboBox.addItem("博士生"); panel.add(label1); panel.add(jTextField); panel.add(jLabel2); panel.add(jComboBox); } private void submit() { // TODO Auto-generated method stub String s1=jTextField.getText().toString().trim(); String s2=textArea.getText().toString().trim(); String s3=jComboBox.getSelectedItem().toString().trim(); String s4=" "; System.out.println(s1); System.out.println(s2); System.out.println(s3); if(radioButton1.isSelected()) { System.out.println("boy"); } if(radioButton2.isSelected()) { System.out.println("gril"); } if(checkBox1.isSelected()) { s4+="Sing "; } if(checkBox2.isSelected()) { s4+="Dance "; } if(checkBox3.isSelected()) { s4+="Draw "; } System.out.println(s4); } private void setPanel3(JPanel panel) { // TODO Auto-generated method stub //String sex = new JLabel(sex, JLabel.CENTER); panel.setPreferredSize(new Dimension(700, 150)); //panel.setLayout(new GridLayout(1, 2)); JLabel sex = new JLabel("sex:", JLabel.CENTER); JPanel selectBox = new JPanel(); selectBox.setPreferredSize(new Dimension(300, 100)); FlowLayout flowLayout = new FlowLayout(); panel.setLayout(flowLayout); selectBox.setBorder(BorderFactory.createTitledBorder("")); selectBox.setLayout(new GridLayout(2, 1)); buttonGroup = new ButtonGroup(); radioButton1=new JRadioButton("boy"); radioButton2=new JRadioButton("gril"); buttonGroup.add(radioButton1); buttonGroup.add(radioButton2); selectBox.add(radioButton1); selectBox.add(radioButton2); panel.add(sex); panel.add(selectBox); } private void setPanel2(JPanel panel) { // TODO Auto-generated method stub panel.setPreferredSize(new Dimension(700, 80)); panel.setLayout(new GridLayout(1, 4)); JLabel jLabel = new JLabel("Address:",JLabel.CENTER); textArea=new JTextArea(); //textArea.setPreferredSize(new Dimension(80, 60)); Label label2 = new Label(" Hobby: ", JLabel.CENTER); JPanel selectBox = new JPanel(); selectBox.setBorder(BorderFactory.createTitledBorder("")); selectBox.setLayout(new GridLayout(3, 1)); checkBox1=new JCheckBox("Sing"); checkBox2=new JCheckBox("Dance"); checkBox3=new JCheckBox("Draw"); selectBox.add(checkBox1); selectBox.add(checkBox2); selectBox.add(checkBox3); panel.add(jLabel); panel.add(textArea); panel.add(label2); panel.add(selectBox); } private void setPanel4(JPanel panel) { // TODO Auto-generated method stub panel.setPreferredSize(new Dimension(750, 50)); button1=new JButton("submit"); button2=new JButton("reset"); FlowLayout flowLayout = new FlowLayout(); //panel.set panel.setLayout(flowLayout); panel.add(button1); panel.add(button2); button1.addActionListener((e) -> submit()); button2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub jTextField.setText(null); textArea.setText(null); jComboBox.setSelectedIndex(0); checkBox1.setSelected(false); checkBox2.setSelected(false); checkBox3.setSelected(false); buttonGroup.clearSelection(); } }); } }
import java.awt.EventQueue; import javax.swing.JFrame; public class Main { public static void main(String[] args) { EventQueue.invokeLater(() ->{ A page=new A(); }); } }
2.创建两个线程,每个线程按顺序输出5次“你好”,每个“你好”要标明来自哪个线程及其顺序号。
class test implements Runnable{ private String name; private int count; public test(String pname) { name=pname; count=0; } @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<5;i++) { count++; System.out.println("hello:"+name+"顺序为:"+count); } } } public class M{ public static void main(String[] args) { test test = new test("来自第一个线程"); test test2 = new test("来自第二个线程"); Thread thread = new Thread(test); Thread thread2 = new Thread(test2); thread.start(); thread2.start(); } }
3. 完善实验十五 GUI综合编程练习程序。
原文地址:https://www.cnblogs.com/fzx201626/p/10126488.html