博文正文开头格式:(2分)
项目 |
内容 |
这个作业属于哪个课程 |
https://www.cnblogs.com/nwnu-daizh/ |
这个作业的要求在哪里 |
https://www.cnblogs.com/nwnu-daizh/p/12031970.html |
作业学习目标 |
(1) 掌握Java应用程序的打包操作; (2) 掌握线程概念; (3) 掌握线程创建的两种技术。 (4) 学习设计应用程序的GUI。 |
第一部分:总结教材14.1-14.3知识内容(20分)
14.1什么是线程
1.进程:正在执行中的程序,其实是应用程序在内存中的那片空间
线程:进程中的一个执行单元,负责进程中程序的运行,一个进程至少有一个线程,一个进程中是可以有多个线程的,这个应用程序也可以成为多线程程序
故一个程序同时执行多个任务,通常每个任务称为一个线程。
2.进程和线程区别
进程拥有自己的一整套变量体系,而线程则是共享。线程是轻量的,创建撤销的开销小。
3.多进程与多线程的区别
二者的本质区别在于每个进程拥有自己的一整套变量,而线程则共享数据,这听起来似乎有些风险,的确也是这样。然而,共享变量使线程之间的通信比进程之间的通信更有效,更容易。除此之外,在有些操作系统中,与进程相比较,线程更“轻量级”,创建、撤销一个线程要比启动一个新进程的开销要小得多。
当然,在实际应用中,多线程也非常有用。例如:一个浏览器可以同时下载几幅图片、一个web服务器需要同时处理几个并发的请求、图形用户界面(GUI)程序用一个独立的线程从宿主环境中收集用户界面的事件等等。
4.线程的简单使用:
方式一:
//自定义Runnable class MyRunnable implements Runnable{ @Override public void run () { //需要处理的任务 } } //创建Runnable对象 Runnable r = new MyRunnable(); //创建Thread对象 Thread t = new Thread(r); //启动线程 t.start();
方式二:
//复写Thread类的run方法 Thread thread= new Thread(){ @Override public void run () { //需要做的任务 } }; thread.start();
方式二每一个任务启动一个线程不好,开销大,多任务并行处理可以通过线程池来实现。
14.2中断线程
线程终止只有两种方式:
1、run方法正常退出,自然死亡
2、因为有没有捕获的异常而终止run方法,意外死亡
stop 方法可以终止线程,但是被弃用了
当线程调用interrupt方法时,线程中断状态被置位;每个线程都有一个boolean标志,以判断线程是否被中断。
中断不等于终止,没有任务和语言上的要求,中断的线程应该终止。中断一个线程只不过是引起他的注意,被中断的线程可以决定如何响应中断。线程可以简单的将中断作为一个终止的请求。
//发送中断请求 void interrupt() //测试当前线程(即正在执行这一命令的线程)是否被中断,副作用是会把当前线程中断状态变成false static boolean interrupted() //测试线程是否被中断,实例方法,不会重置中断状态 boolean isInterrupted()
线程被阻塞时中断状态无法被检测,会抛出中断异常InterruptedException;通常是sleep,wait,IO阻塞等的情况下出现。
14.3 线程状态
1、线程一共有如下6种状态:
New (新建)
Runnable (可运行)
Blocked (被阻塞)
Waiting (等待)
Timed waiting (计时等待)
Terminated (被终止)
1.新创建线程:
线程的创建
new(新建) 线程对象刚刚创建,还没有启动,此时还处于不可运行状态。
Thread thread=new Thread(“test”) 此时线程thread处于新建状态,但已有了相应的内存空间以及其它资源。
通过API中的英文Thread类的查询,通过对Thread类的描述,了解到线程的创建有两种方式
(1)、继承Thread类
1.1、定义一个类,继承thread
1.2、重写run()方法
1.3、创建子类对象,就是创建线程对象
1.4、调用start方法,开启线程并让线程执行,同时还会告诉JVM调用run方法
(2)、线程创建的第二种方式:实现runable接口
2.1、定义类实现runable接口
2.2、覆盖接口中的run方法,将线程任务代码定义到run方法中
2.3、创建Thread类的对象
2.4、将runable接口的子类对象作为参数传递给Thread类的构造函数
2.5、调用Thread类的start方法开启线程
2.创建线程的目的:
是为了建立单独的执行路径,让多部分代码实现同时执行,也就是说,线程的创建并执行需要给定的代码(线程的任务)
对于之前创建的主线程,其任务定义在主函数中自定义的线程需要执行的任务都定义在run方法中Thread类中的run方法内部的任务并不是我们所需要的,只要重写这个run方法即可
既然Thread类已经定义了线程任务的位置,只要在位置中定义任务代码即可,所以进行了重写run方法的动作
多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间
当执行线程的任务结束了,线程自动在栈内存中释放,但当所有的线程都结束了,那么进程也就结束了
3.获取线程名称:
Thread:currentThread()获取当前线程对象。
Thread.currentThread().getName();
4.几个函数:
void join()//等待终止指定线程,让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作
void join(long millis) //等待指定的线程死亡或者经过指定的毫秒数,如果超过这个时间,则停止等待,变为可运行状态
void stop()//停止该线程。这一方法已过时
void suspend() //暂停这一线程的执行 已过时
void resume() //恢复线程,和suspend一起使用,也过时
void sleep(long millis)// 等待休眠millis 毫秒,执行这个方法,线程进入计时等待
void yield()//暂停当前正在执行的线程对象,并执行其他线程。理论上,yield意味着放手,放弃,投降。
//一个调用yield()方法的线程告诉虚拟机它乐意让其他线程占用自己的位置。这表明该线程没有在做一些紧急的事情。
//注意,这仅是一个暗示,并不能保证不会产生任何影响。
第二部分:实验部分
实验1:测试程序1(10分)
在elipse IDE中调试运行教材585页程序13-1,结合程序运行结果理解程序;
将所生成的JAR文件移到另外一个不同的目录中,再运行该归档文件,以便确认程序是从JAR文件中,而不是从当前目录中读取的资源。
13-1代码如下:
package resource; import java.awt.*; import java.io.*; import java.net.*; import java.util.*; import javax.swing.*; /** * A frame that loads image and text resources. * 加载图像和文本资源的框架。 */ public class ResourceTest { public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame frame = new ResourceTestFrame(); frame.setTitle("ResourceTest"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); } } class ResourceTestFrame extends JFrame { private static final int DEFAULT_WIDTH = 300; private static final int DEFAULT_HEIGHT = 300; public ResourceTestFrame() { setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); URL aboutURL = getClass().getResource("about.gif"); Image img = new ImageIcon(aboutURL).getImage(); //利用about.gif图像文件制作图标; setIconImage(img); //进行资源加载时从外村到内存 JTextArea textArea = new JTextArea(); InputStream stream = getClass().getResourceAsStream("about.txt"); //读取about.gif(当前项目目录下的文件名文件 try (Scanner in = new Scanner(stream, "UTF-8"))//规定编码标准 { while (in.hasNext()) textArea.append(in.nextLine() + "\n");//调用append方法让内容显示到文本区 } add(textArea); } }
运行截图:
归档:
按照如下步骤创建和引入JAR文件:
1. 创建储存包文件的目录。与利用CLASSPATH创建包文件目录的要求相同,从总目录开始,必须至少有3个子目录。如:C:\javabook\classes\ch12\
2. 将包文件(源代码文件)复制到以上目录中。
3. 分别编译每个程序。
4. 创建JAR文件。输入如下jar指令:
C:\javabook\classes\jar cvf ch12.jar ch12\*
这个指令在当前目录中产生一个包括子目录所有文件的名为ch12.jar的压缩文件。
三个jar指令的选项cvf的含义为:c——创建JAR文件;v——显示创建过程信息;f——跟随的是jar文件名。“*”表示将包括源代码在内的程序都压缩在JAR文件中。
也可以利用如下指令只包括字节文件:
C:\javabook\classess\jar cvf ch12.jar ch12\*.class
注意,一般JAR文件名使用小写字母。可以是任何Java合法标识符。
5. 将创建的jar文件拷贝到JDK的子目录\jre\lib\ext\中。
6. 实现引入。任何程序都可以引入这个JAR文件中的包类。
实验1:测试程序2(10分)
代码如下:
class Lefthand extends Thread { public void run() { for(int i=0;i<=5;i++) { System.out.println("You are Students!"); try{ sleep(500); }//输出第一次后,程序休眠时间为500ms; catch(InterruptedException e) { System.out.println("Lefthand error.");} } //for循环执行6次,输出六次You are Students! } } class Righthand extends Thread { public void run() { for(int i=0;i<=5;i++) { System.out.println("I am a Teacher!"); try{ sleep(300); }//输出第一次后,程序休眠时间为300ms; catch(InterruptedException e) { System.out.println("Righthand error.");} }//for循环执行6次,输出六次I am a Teacher! } } 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 Lefthand1 implements Runnable{ public void run() { for(int i=0;i<=5;i++) { System.out.println("You are Students!"); try{ Thread.sleep(500); } //输出第一次后,程序休眠时间为500ms; catch(InterruptedException e)//中断异常 { System.out.println("Lefthand error.");} } } } class Righthand1 implements Runnable { public void run() { for(int i=0;i<=5;i++) { System.out.println("I am a Teacher!"); try{ Thread.sleep(300); } //输出第一次后,程序休眠时间为300ms; catch(InterruptedException e) { System.out.println("Righthand error.");} } } } public class ThreadTest1 { static Thread left; static Thread right; public static void main(String[] args) { Runnable lefthand = new Lefthand(); left=new Thread(lefthand); left.start(); Runnable righthand = new Righthand(); right=new Thread(righthand); right.start(); } }
运行结果:
实验1:测试程序3(10分)
14-1代码如下:
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; /** *用显示弹跳球的组件构建框架 *开始和关闭按钮 */ 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); pack(); } /** *向容器中添加按钮。 * @参数c容器 * @ param title按钮标题 * @ param侦听器按钮的操作侦听器 */ 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. *向面板添加一个弹跳球,使其弹跳1000次。 */ 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.*; /** * 在矩形边缘移动和反弹的球 * @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;//设置四个属性的初值 /** * 将球移动到下一个位置,如果球碰到其中一个边缘,则反转方向 */ public void move(Rectangle2D bounds) { x += dx; y += dy; if (x < bounds.getMinX()) { x = bounds.getMinX(); dx = -dx;//x比所设置框的行的左边最小值还小时,变换方向 } if (x + XSIZE >= bounds.getMaxX()) { x = bounds.getMaxX() - XSIZE; dx = -dx;//x比所设置框的行的右边最大值还大时,变换方向 } if (y < bounds.getMinY()) { y = bounds.getMinY(); dy = -dy;//y比所设置框的列的最小值还小时,变换方向 } if (y + YSIZE >= bounds.getMaxY()) { y = bounds.getMaxY() - YSIZE; dy = -dy;//y比所设置框的列的最大值还大时,变换方向 } } /** *获取球在其当前位置的形状。 */ public Ellipse2D getShape() { return new Ellipse2D.Double(x, y, XSIZE, YSIZE); } }
package bounce; import java.awt.*; import java.util.*; import javax.swing.*; /** * The component that draws the balls. * 拉动球的组件。 */ public class BallComponent extends JPanel { private static final int DEFAULT_WIDTH = 450; private static final int DEFAULT_HEIGHT = 350; private java.util.List<Ball> balls = new ArrayList<>(); /** *向组件添加一个球。 * @ param b要添加的球 */ public void add(Ball b) { balls.add(b); } public void paintComponent(Graphics g) //调用paintComponent方法来进行自定义 { super.paintComponent(g); // erase background擦出背景 Graphics2D g2 = (Graphics2D) g; for (Ball b : balls) { g2.fill(b.getShape()); } } public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); } }
运行截图:
14-4代码如下:
package bounceThread; import java.awt.*; import java.util.*; import javax.swing.*; import bounce.Ball; /** * The component that draws the balls. * 拉动球的组件。 */ public class BallComponent extends JPanel { private static final int DEFAULT_WIDTH = 450; private static final int DEFAULT_HEIGHT = 350; private java.util.List<Ball> balls = new ArrayList<>(); /** *向组件添加一个球。 * @ param b要添加的球 */ public void add(Ball b) { balls.add(b); } public void paintComponent(Graphics g) //调用paintComponent方法来进行自定义 { super.paintComponent(g); // erase background擦出背景 Graphics2D g2 = (Graphics2D) g; for (Ball b : balls) { g2.fill(b.getShape()); } } public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); } }
package bounceThread; import java.awt.geom.*; /** * 在矩形边缘移动和反弹的球 * @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;//设置四个属性的初值 /** * 将球移动到下一个位置,如果球碰到其中一个边缘,则反转方向 */ public void move(Rectangle2D bounds) { x += dx; y += dy; if (x < bounds.getMinX()) { x = bounds.getMinX(); dx = -dx;//x比所设置框的行的左边最小值还小时,变换方向 } if (x + XSIZE >= bounds.getMaxX()) { x = bounds.getMaxX() - XSIZE; dx = -dx;//x比所设置框的行的右边最大值还大时,变换方向 } if (y < bounds.getMinY()) { y = bounds.getMinY(); dy = -dy;//y比所设置框的列的最小值还小时,变换方向 } if (y + YSIZE >= bounds.getMaxY()) { y = bounds.getMaxY() - YSIZE; dy = -dy;//y比所设置框的列的最大值还大时,变换方向 } } /** *获取球在其当前位置的形状。 */ public Ellipse2D getShape() { return new Ellipse2D.Double(x, y, XSIZE, YSIZE); } }
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() { // BounceFrame有两个组件BallComponent和buttonPanel comp = new BallComponent(); add(comp, BorderLayout.CENTER); JPanel buttonPanel = new JPanel();//buttonPanel有两个组件start和close addButton(buttonPanel, "Start", event -> addBall()); addButton(buttonPanel, "Close", event -> System.exit(0)); add(buttonPanel, BorderLayout.SOUTH); pack(); } /** *向容器中添加按钮。 * @参数c容器 * @ param title按钮标题 * @ param侦听器按钮的操作侦听器 */ public void addButton(Container c, String title, ActionListener listener)//c为所加的组件 { JButton button = new JButton(title);//利用了抽象思维方式 c.add(button); button.addActionListener(listener); } /** * 在画布上添加一个弹跳球,并开始一条线使其弹跳 */ public void addBall() { bounce.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(); }//将弹跳球代码放在一个独立的线程中,实现一个类BallRunnable,然后将动画代码放在run方法中 }
运行截图:
根据实验结果可知,加入了线程之后,只要点击start按钮,就会有小球出现,而不加线程时, 至于当前面的一个小球开始运动至结束后,才会出现下一个小球;
实验2:结对编程练习:采用GUI界面设计以下程序,并创建程序归档文件。
设计一个100以内整数小学生四则运算练习程序,由计算机随机产生10道加减乘除练习题,学生输入答案,由程序检查答案是否正确,每道题正确计10分,错误不计分,10道题测试结束后给出测试总分;
将程序中测试练习题及学生答题结果输出到文件,文件名为test.txt。
1)程序设计思路简述;
此次结对编程设计的GUI界面,主要是用了网格布局,在其“NORTH”是创建了一个一行三列的面板,来分别存放题目,用户输入的答案以及判断其答案是否正确,然后在“SOUTH”设计了一个三个按钮,按钮的作用分别是“出题”,“判断正误”以及“生成文件”,使得最后将程序中测试练习题及学生答题结果输出到文件,文件名为test.txt;最后在界面中加入了一个文本域,来将即时的题目对错情况和得分情况进行记录;总的设计思路便是如此,主函数用的是lanmda表达式。
2) 程序代码如下:
package Test; import java.awt.*; import javax.swing.*; public class CeshiTest { public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame frame = new Ceshi(); frame.setTitle("四则运算器"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); } }
package Test; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import javax.swing.*; public class Ceshi extends JFrame { int i = 0, m = 0; int n = 0, sum = 0; private PrintWriter out = null; private JTextArea display; private JScrollPane text; private String[] exam = new String[50]; private String[] answer = new String[10]; public Ceshi() { JPanel panel = new JPanel(); panel.setLayout(new GridLayout(1, 3)); JTextField timu = new JTextField(); JTextField daan = new JTextField(20); JTextField panduan = new JTextField(); panel.add(timu); panel.add(daan); panel.add(panduan); add(panel, BorderLayout.NORTH); JPanel panel1 = new JPanel(); panel1.setLayout(new GridLayout(1, 3)); Button button1 = new Button("生成题目"); Button button2 = new Button("判断正误"); Button button3 = new Button("生成文件"); panel1.add(button1); panel1.add(button2); panel1.add(button3); add(panel1, BorderLayout.SOUTH); setSize(500, 400); button1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { daan.setText(null); panduan.setText(null); if (i < 10) { int a = (int) (Math.random() * 100); int b = (int) (Math.random() * 100); //随机产生100以内整数 int m = (int) Math.round(Math.random() * 3); switch (m) { case 0: while (b == 0 || a % b != 0) { b = (int) Math.round(Math.random() * 100); a = (int) Math.round(Math.random() * 100); //100以内整数 } timu.setText("第" +(i+1)+"道" + ": " + a + "/" + b + "="); exam[i] = timu.getText(); n = a / b; i++; break; case 1: timu.setText("第" +(i+1)+"道" + ": " + a + "*" + b + "="); exam[i] = timu.getText(); n = a * b; i++; break; case 2: timu.setText("第" +(i+1)+"道" + ": " + a + "+" + b + "="); exam[i] = timu.getText(); n = a + b; i++; break; case 3: while (a < b) { int x = a; a = b; b = x; } timu.setText("第" +(i+1)+"道" + ": " + a + "-" + b + "="); exam[i] = timu.getText(); n = a - b; i++; break; } } } }); button2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (i < 11) { int input = Integer.parseInt(daan.getText());//用户输入计算答案 String d =daan.getText().toString().trim();//输入答案 if (daan.getText() != " ") { if (input == n) { sum += 10; display.append("当前成绩为" + sum+"\n"); panduan.setText("答案正确"); } else display.append("没有成绩"+ "\n"); panduan.setText("答案错误"); } answer[m] = d; m++; } } }); button3.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { out = new PrintWriter("C:\\Users\\java\\Desktop\\text.txt"); } catch (FileNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace();//输出路径 } for (int counter = 0; counter < 10; counter++) { out.write(exam[counter] + answer[counter]+"\n"); } out.println("最终运算成绩为" + sum); out.close(); } }); display=new JTextArea("运算结果显示区:",60,10); text= new JScrollPane(display); display.setText(""); display.append("当前运算成绩为" + sum+ "\n"); System.out.println(sum); add(text,BorderLayout.CENTER); } }
3)运行结果:
对文件进行归档:
实验总结:(15分)
这周学习了第十四章有关线程的基础知识点,在理论课中首先对线程的概念有了了解,然后通过老师课堂的讲解和在实验课中对课本范例的运行,对有关线程的代码中的知识点有了一定的认识,其次,
掌握线程创建的两种技术:继承Thread类和实现runable接口,通过实验中弹跳球的例子进行比较学习,对二者的区别和不同有了更深的掌握, 并且还通过结对编程的作业学习了设计应用程序的GUI。通过对知
识点的总结和应用对这一周所学的这一章的内容也有了更多的掌握。