第十六周总结java
1、知识总结编程
方式1:继承java.lang.Thread类,并覆盖run()方法。优点:编写简单;劣势:没法继承其余父类安全
方式2:实现java.lang.Runnable接口,并实现run()方法。优点:能够继承其余类,多线程能够共享同一个Thread对象;劣势:编程方式稍微复杂,如需访问当前线程,需调用Thread.currentThread()方法数据结构
两种方法的区别多线程
1) start:并发
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。经过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并无运行,一旦获得cpu时间片,就开始执行run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。框架
2) run:dom
run()方法只是类的一个普通方法而已,若是直接调用run方法,程序中依然只有主线程这一个线程,其程序执行路径仍是只有一条,仍是要顺序执行,仍是要等待jvm
run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。ide
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,仍是在主线程里执行。
这两个方法应该都比较熟悉,把须要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。而且run()方法必须是public访问权限,返回值类型为void。
两种方式的比较 :
实际中每每采用实现Runable接口,一方面由于java只支持单继承,继承了Thread类就没法再继续继承其它类,并且Runable接口只有一个run方法;另外一方面经过结果能够看出实现Runable接口才是真正的多线程。
线程是一个动态执行的过程,它也有一个从生产到死亡的过程。
(1)生命周期的五种状态
当建立Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。
例如:Thread t1 = new Threade();
线程已经被启动(start),正在等待分配CPU时间片,也就是说此事线程正在就绪队列中排队等候获得CPU资源。
例如:t1.start();
运行(running)
线程得到cpuz资源正在执行任务(run()方法),此时除非线程自动放弃CPU资源或者有优先级更高的的线程进入,线程将一直运行到结束!
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
天然终止:正常运行run()方法后终止
异常终止:调用stop()方法让一个线程终止运行
因为某种缘由致使正在运行的线程让出CPU并暂停本身的执行,即进入堵塞状态。
正在睡眠:用sleep(long t) 方法可以使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。
正在等待:调用wait()方法。(调用notify()方法回到就绪状态)
被另外一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)
当多个线程访问同一个数据时,容易出现线程安全问题,须要某种方式来确保资源在某一时刻只被一个线程使用。须要让线程同步,保证数据安全
线程同步的实现方案:同步代码块和同步方法,均须要使用synchronized关键字
线程同步的好处:解决了线程安全问题
线程同步的缺点:性能降低,可能会带来死锁
Java中每一个对象都有一个内置锁。
当程序运行到非静态的synchronized同步方法上时,自动得到与正在执行代码类的当前实例(this实例)有关的锁。得到一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。
当程序运行到synchronized同步方法或代码块时才该对象锁才起做用。
一个对象只有一个锁。因此,若是一个线程得到该锁,就没有其余线程能够得到锁,直到第一个线程释放(或返回)锁。这也意味着任何其余线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
释放锁是指持锁线程退出了synchronized同步方法或代码块。
关于锁和同步,有一下几个要点:
1)、只能同步方法,而不能同步变量和类;
2)、每一个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪一个对象上同步?
3)、没必要同步类中全部的方法,类能够同时拥有同步和非同步方法。
4)、若是两个线程要执行一个类中的synchronized方法,而且两个线程使用相同的实例来调用方法,那么一次只能有一个线程可以执行方法,另外一个须要等待,直到锁被释放。也就是说:若是一个线程在对象上得到一个锁,就没有任何其余线程能够进入(该对象的)类中的任何一个同步方法。
5)、若是线程拥有同步和非同步方法,则非同步方法能够被多个线程自由访问而不受锁的限制。
6)、线程睡眠时,它所持的任何锁都不会释放。
7)、线程能够得到多个锁。好比,在一个对象的同步方法里面调用另一个对象的同步方法,则获取了两个对象的同步锁。
8)、同步损害并发性,应该尽量缩小同步范围。同步不但能够同步整个方法,还能够同步方法中一部分代码块。
9)、在使用同步代码块时候,应该指定在哪一个对象上同步,也就是说要获取哪一个对象的锁。例如:
public int fix(int y) {
synchronized (this) {
x = x - y;
}
return x;
}
固然,同步方法也能够改写为非同步方法,但功能彻底同样的,例如:
public synchronized int getX() {
return x++;
}
与
public int getX() {
synchronized (this) {
return x;
}
}
效果是彻底同样的。
sleep()是让某个线程暂停运行一段时间,其控制范围是由当前线程决定,也就是说,在线程里面决定.比如如说,我要作的事情是 "点火->烧水->煮面",而当我点完火以后我不当即烧水,我要休息一段时间再烧.对于运行的主动权是由个人流程来控制。
而wait(),首先,这是由某个肯定的对象来调用的,将这个对象理解成一个传话的人,当这我的在某个线程里面说"暂停!",也是 thisObj.wait(),这里的暂停是阻塞,仍是"点火->烧水->煮饭",thisObj就比如一个监督个人人站在我旁边,原本该线 程应该执行1后执行2,再执行3,而在2处被那个对象喊暂停,那么我就会一直等在这里而不执行3,但正个流程并无结束,我一直想去煮饭,但还没被容许, 直到那个对象在某个地方说"通知暂停的线程启动!",也就是thisObj.notify()的时候,那么我就能够煮饭了,这个被暂停的线程就会从暂停处 继续执行。
其实二者均可以让线程暂停一段时间,可是本质的区别是一个线程的运行状态控制,一个是线程之间的通信的问题。
2、实验部分——线程技术
1、实验目的与要求
(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(); } } |
修改后的代码
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 { public static void main(String[] args) { Runnable left = new Lefthand(); Thread a = new Thread(left); Runnable right = new Righthand(); Thread b = new Thread(right); a.start(); b.start(); } }
测试程序2:
l 在Elipse环境下调试教材625页程序14-一、14-2 、14-3,结合程序运行结果理解程序;
l 在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 bounce; 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 JPanel { 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 component. * @param b the ball to add */ public void add(Ball b) { balls.add(b); } public void paintComponent(Graphics g) { 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 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());//将按钮放入buttonPanel 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()); comp.paint(comp.getGraphics()); Thread.sleep(DELAY);//在两个球显示之间有延迟 } } catch (InterruptedException e)//中断异常 { } } }
14-一、14-二、14-3,运行结果以下:(小球没有停下的时候程序关闭不了)
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(); } }
14-4程序运行结果以下:(小球运动时程序也能够被关闭)
测试程序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-五、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 initialBalance 每一个帐户的初始余额 */ public Bank(int n, double initialBalance) { accounts = new double[n]; Arrays.fill(accounts, initialBalance); } /** * 把钱从一个帐户转到另外一个帐户 * @param from 转帐帐户从 * @param to 转帐帐户到 * @param amount 转让的数额 */ 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) 用户信息输入界面以下图所示:
(2) 用户点击提交按钮时,用户输入信息显示控制台界面;
(3) 用户点击重置按钮后,清空用户已输入信息;
(4) 点击窗口关闭,程序退出。
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); } }
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(); } }
2.建立两个线程,每一个线程按顺序输出5次“你好”,每一个“你好”要标明来自哪一个线程及其顺序号。
class Lefthand implements Runnable { public void run() { for (int i = 1; i <= 5; i++) { System.out.println(i+"a.你好"); try { Thread.sleep(500); } catch (InterruptedException e) { System.out.println("error."); } } } } class Righthand implements Runnable { public void run() { for (int i = 1; i <= 5; i++) { System.out.println(i+"b.你好"); try { Thread.sleep(300); } catch (InterruptedException e) { System.out.println("error."); } } } } public class ThreadTest { static Thread left; static Thread right; public static void main(String[] args) { Runnable a = new Lefthand(); Runnable b = new Righthand(); left = new Thread(a); right = new Thread(b); left.start(); right.start(); } }
3、实验总结
经过本次实验,我大体掌握了线程的概念和线程建立的两种技术,理解了线程的优先级属性及调度方法,在学习过程当中我也意识到了多线程对java编程的重要性,必定好好学习这一章知识。