实验十六  线程技术

实验时间 2017-12-8

1、实验目的与要求

(1) 掌握线程概念;

‐多线程 是进程执行过中产生的多条线索。 是进程执行过中产生的多条线索。 是进程执行过中产生的多条线索。 是进程执行过中产生的多条线索。
‐线程是比进执行更小的单位。
‐线程不能独立存在,必须于进 中同一线程不能独立存在,必须于进 中同一线程不能独立存在,必须于进 中同一线程不能独立存在,必须于进 中同一程的各线间共享进空数据。 程的各线间共享进空数据。
‐每个线程有它自身的产生、存在和消亡过, 每个线程有它自身的产生、存在和消亡过, 每个线程有它自身的产生、存在和消亡过, 每个线程有它自身的产生、存在和消亡过, 是一个动态的概念。
‐多线程意味着一个序的行语句可以看上去几 多线程意味着一个序的行语句可以看上去几 多线程意味着一个序的行语句可以看上去几 多线程意味着一个序的行语句可以看上去几 乎在同一时间内运行。
‐线程创建、销毁和切换的负荷远小于进,又称 线程创建、销毁和切换的负荷远小于进,又称 线程创建、销毁和切换的负荷远小于进,又称 线程创建、销毁和切换的负荷远小于进,又称 为轻量级进程( lightweight process lightweight process lightweight process )。

(2) 掌握线程创建的两种技术;

Java 实现多线程有两种途径:
‐创建 Thread 类的子

  首先需从 Thread 类派生出一个子, 在该子类中 重写 run() 方法。
  例:
  class class hand extends Thread hand extends Thread hand extends Threadhand extends Thread hand extends Thread hand extends Thread hand extends Thread hand extends Thread hand extends Thread                hand extends Thread
  {    
  public void run() public void run() public void run() public void run() public void run()
  {……} {……}
  }
  然后用创建该子类的对象
  Lefthand left=new Lefthand ();
  Righthand right=new Righthand ();
  . 最后用 start() 方法启动线程
  left.start ();
  right.start ();

‐在程序中定义实现 Runnable 接口的类

  首先设计一个实现 Runnable Runnable 接口的类; 接口的类; 接口的类; 接口的类; 接口的类;
  . 然后在类中根据需要重写 run 方法;
  . 再创建该类对象,以此为参数立 再创建该类对象,以此为参数立 ThreadThread Thread类的对象;
  . 调用 Thread Thread Thread 类对象的 start start start方法启动线程,将 方法启动线程,将 方法启动线程,将 方法启动线程,将 方法启动线程,将 方法启动线程,将 方法启动线程,将 方法启动线程,将 CPU 执                              行权转交到 run 方法。 方法。 方法。
   用Runnable() (见教材 (见教材 (见教材 632 页) 接口实现线程 接口实现线程 接口实现线程 接口实现线

(3) 理解和掌握线程的优先级属性及调度方法;

  Java 提供一个 线程调度器 来监控程序启动后进入 来监控程序启动后进入 来监控程序启动后进入 来监控程序启动后进入 来监控程序启动后进入 来监控程序启动后进入 来监控程序启动后进入 来监控程序启动后进入              监控程序启动后进入 来监控程序启动后进入 可运行状态的 所有线程。调度器按照可运行状态的 所有线程。调度器按照可运行状态的 所有线程。调度器按照可运行状态的 所有线程。调度器按照优先级决定应调       度 哪些线程来执行。 优先级决定应调度哪些线程来执行。
– 处于可运行状态的线程首先进入 就绪队 列排就绪队 列排就绪队 列排就绪队 列排就绪队 列排就绪队 列排等 候处理器资源,同一时刻在就绪队列中的线程可 候处理器资源,同一时刻在就绪队列中的线程可 候处理器资源,同一时刻在就绪队列中的线程可 候处理器资源,同一时刻在就绪队列中的线程可 能有多个。 Java 的多线程系统会给每个自动 的多线程系统会给每个自动 的多线程系统会给每个自动 的多线程系统会给每个自动 的多线程系统会给每个自动 的多线程系统会给每个自动 的多线程系统会给每个自动 的多线程系统会给每个自动 的多线程系统会给每个自动 的多线程系统会给每个自动 的多线程系统会给每个自动 的多线程系统会给每个自动 的多线程系统会给每个自动 的多线程系统会给每个自动 分配一个线程的优先级。

Java 的线程调度采用优先级策略 :
 优先级高的执行,低后;
 多线程系统会自动为每个分配一优先级,缺省时,继承其父类的优先级;
 任务紧急的线程,其优先级较高;
 同优先级的线程按“进出”队列原则;
多线程调度

(4) 掌握线程同步的概念及实现技术;

多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 多线程并发运行不确定性问题解决方案: 引入线 引入线 引入线 程同步机制 程同步机制 程同步机制 程同步机制 程同步机制 ,使得另一线程要用该方法 使得另一线程要用该方法 使得另一线程要用该方法 使得另一线程要用该方法 使得另一线程要用该方法 使得另一线程要用该方法 使得另一线程要用该方法 使得另一线程要用该方法 使得另一线程要用该方法 使得另一线程要用该方法 使得另一线程要用该方法 使得另一线程要用该方法 ,就只 能等待 。
 在Java 中解决多线程同步问题 的方法有两种: 的方法有两种: 的方法有两种: 的方法有两种: 的方法有两种: 的方法有两种: 的方法有两种:
-Java SE 5.0中引入 ReentrantLock ReentrantLock ReentrantLockReentrantLock ReentrantLock类(P 648648648页)。
-在共享内存的类方法前加 在共享内存的类方法前加 在共享内存的类方法前加 在共享内存的类方法前加 在共享内存的类方法前加 在共享内存的类方法前加 在共享内存的类方法前加 在共享内存的类方法前加 在共享内存的类方法前加 在共享内存的类方法前加 在共享内存的类方法前加 synchronizedsynchronizedsynchronized synchronized synchronized synchronizedsynchronized修饰符 修饰符 修饰符 。

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();

}

}

 package test;

 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)
{ Runnable left1 = new Lefthand();
Runnable right1 = new Righthand() ;
Thread left = new Thread(left1);
Thread right = new Thread(right1);
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.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());
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 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 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 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 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()+"已计算完毕!");

}

}

测试程序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);
t.start();
}
}
}

综合编程练习

编程练习1

  1. 设计一个用户信息采集程序,要求如下:
 package project1;

 import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*; public class test extends JFrame {
public test() {
JPanel panel1 = new JPanel();
panel1.setPreferredSize(new Dimension(700, 45));
panel1.setLayout(new GridLayout(1, 4));
JLabel label1 = new JLabel("Name:");
JTextField j1 = new JTextField("");
JLabel label2 = new JLabel("Qualification:");
JComboBox<Object> j2 = new JComboBox<>();
j2.addItem("Graduate");
j2.addItem("Not Graduate");
panel1.add(label1);
panel1.add(j1);
panel1.add(label2);
panel1.add(j2); JPanel panel2 = new JPanel();
panel2.setPreferredSize(new Dimension(700, 50));
panel2.setLayout(new GridLayout(1, 4));
JLabel label3 = new JLabel("Address:");
JTextArea j3 = new JTextArea();
JLabel label4 = new JLabel("Hobby:");
JPanel p = new JPanel();
p.setLayout(new GridLayout(3, 1));
p.setBorder(BorderFactory.createLineBorder(null));
JCheckBox c1 = new JCheckBox("Reading");
JCheckBox c2 = new JCheckBox("Singing");
JCheckBox c3 = new JCheckBox("Dancing");
p.add(c1);
p.add(c2);
p.add(c3);
panel2.add(label3);
panel2.add(j3);
panel2.add(label4);
panel2.add(p); JPanel panel3 = new JPanel();
panel3.setPreferredSize(new Dimension(700, 150));
FlowLayout flowLayout1 = new FlowLayout(FlowLayout.CENTER, 50, 10);
panel3.setLayout(flowLayout1);
JLabel label5 = new JLabel("Sex:");
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(2, 1));
p1.setBorder(BorderFactory.createLineBorder(null));
ButtonGroup bu = new ButtonGroup();
JRadioButton jr1 = new JRadioButton("Male");
JRadioButton jr2 = new JRadioButton("Female");
bu.add(jr1);
bu.add(jr2);
p1.add(jr1);
p1.add(jr2);
panel3.add(label5);
panel3.add(p1);
add(panel1);
add(panel2);
add(panel3); JPanel panel4 = new JPanel();
panel4.setPreferredSize(new Dimension(700, 150));
JButton b1 = new JButton("Validate");
panel4.add(b1);
JButton b2 = new JButton("Reset");
panel4.add(b2);
add(panel4); FlowLayout flowLayout = new FlowLayout();
this.setLayout(flowLayout);
this.setTitle("Students Detail");
this.setBounds(200, 200, 800, 400);
this.setVisible(true);
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); b1.addActionListener(new ActionListener() { @Override
public void actionPerformed(ActionEvent e) {
String xueli = j2.getSelectedItem().toString();
System.out.println("Name:" + j1.getText());
System.out.println("Qualification:" + xueli);
String hobbystring = "Hobby:";
if (c1.isSelected()) {
hobbystring += "Reading";
}
if (c2.isSelected()) {
hobbystring += "Singing";
}
if (c3.isSelected()) {
hobbystring += "Dancing";
}
System.out.println("Address:" + j3.getText());
if (jr1.isSelected()) {
System.out.println("Sex:Male");
}
if (jr2.isSelected()) {
System.out.println("Sex:Female");
}
System.out.println(hobbystring);
}
});
b2.addActionListener(new ActionListener() { @Override
public void actionPerformed(ActionEvent e) {
j1.setText(null);
j3.setText(null);
j2.setSelectedIndex(0);
c1.setSelected(false);
c2.setSelected(false);
c3.setSelected(false);
bu.clearSelection();
}
});
} public static void main(String args[]) {
new test();
} }

(1) 用户信息输入界面如下图所示:

(2) 用户点击提交按钮时,用户输入信息显示控制台界面;

(3) 用户点击重置按钮后,清空用户已输入信息;

(4) 点击窗口关闭,程序退出。

2.创建两个线程,每个线程按顺序输出5次“你好”,每个“你好”要标明来自哪个线程及其顺序号。

 package project2;

 class Lefthand implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(i + 1 + ":a.你好!");
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 + 1 + ":b.你好!");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
System.out.println("Righthand error.");
}
}
}
} public class test {
public static void main(String[] args) {
Runnable left = new Lefthand();
Thread l = new Thread(left);
Runnable right = new Righthand();
Thread r = new Thread(right);
l.start();
r.start();
}
}

3. 完善实验十五 GUI综合编程练习程序。

练习一:

 package project3;

 public class Student implements Comparable<Student> {

     private String name;
private String number ;
private String sex ;
private int age;
private String province; public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getnumber() {
return number;
}
public void setnumber(String number) {
this.number = number;
}
public String getsex() {
return sex ;
}
public void setsex(String sex ) {
this.sex =sex ;
}
public int getage() { return age;
}
public void setage(int age) {
// int a = Integer.parseInt(age);
this.age= age;
} public String getprovince() {
return province;
}
public void setprovince(String province) {
this.province=province ;
} public int compareTo(Student o) {
return this.name.compareTo(o.getName());
} public String toString() {
return name+"\t"+sex+"\t"+age+"\t"+number+"\t"+province+"\n";
}
}
 package project3;

 import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Scanner;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*; public class Main extends JFrame {
private static ArrayList<Student> studentlist;
private static ArrayList<Student> list;
private JPanel panel;
private JPanel buttonPanel;
private static final int DEFAULT_WITH = 600;
private static final int DEFAULT_HEIGHT = 300; public Main() {
studentlist = new ArrayList<>();
Scanner scanner = new Scanner(System.in);
File file = new File("C:\\Users\\ASUS\\Desktop\\身份证号.txt");
try {
FileInputStream fis = new FileInputStream(file);
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
String temp = null;
while ((temp = in.readLine()) != null) { Scanner linescanner = new Scanner(temp); linescanner.useDelimiter(" ");
String name = linescanner.next();
String number = linescanner.next();
String sex = linescanner.next();
String age = linescanner.next();
String province = linescanner.nextLine();
Student student = new Student();
student.setName(name);
student.setnumber(number);
student.setsex(sex);
int a = Integer.parseInt(age);
student.setage(a);
student.setprovince(province);
studentlist.add(student); }
} catch (FileNotFoundException e) {
System.out.println("学生信息文件找不到");
e.printStackTrace();
} catch (IOException e) {
System.out.println("学生信息文件读取错误");
e.printStackTrace();
}
panel = new JPanel();
panel.setLayout(new BorderLayout());
JTextArea jt = new JTextArea();
panel.add(jt);
add(panel, BorderLayout.NORTH);
buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(1, 7));
JButton jButton = new JButton("字典排序");
JButton jButton1 = new JButton("年龄最大和年龄最小");
JLabel lab = new JLabel("猜猜你的老乡");
JTextField jt1 = new JTextField();
JLabel lab1 = new JLabel("找找同龄人(年龄相近):");
JTextField jt2 = new JTextField();
JLabel lab2 = new JLabel("输入你的身份证号码:");
JTextField jt3 = new JTextField();
JButton jButton2 = new JButton("退出");
jButton.setBounds(110, 90, 60, 30);
jButton1.setBounds(110, 90, 60, 30);
jt1.setBounds(110, 90, 60, 30);
jt2.setBounds(110, 90, 60, 30);
jt3.setBounds(110, 90, 60, 30);
jButton2.setBounds(110, 90, 60, 30);
jButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Collections.sort(studentlist);
jt.setText(studentlist.toString());
}
});
jButton1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int max = 0, min = 100;
int j, k1 = 0, k2 = 0;
for (int i = 1; i < studentlist.size(); i++) {
j = studentlist.get(i).getage();
if (j > max) {
max = j;
k1 = i;
}
if (j < min) {
min = j;
k2 = i;
} }
jt.setText("年龄最大:" + studentlist.get(k1) + "年龄最小:" + studentlist.get(k2));
}
});
jButton2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dispose();
System.exit(0);
}
});
jt1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String find = jt1.getText();
String text="";
String place = find.substring(0, 3);
for (int i = 0; i < studentlist.size(); i++) {
if (studentlist.get(i).getprovince().substring(1, 4).equals(place)) {
text+="\n"+studentlist.get(i);
jt.setText("老乡:" + text);
}
}
}
});
jt2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String yourage = jt2.getText();
int a = Integer.parseInt(yourage);
int near = agenear(a);
int value = a - studentlist.get(near).getage();
jt.setText("年龄相近:" + studentlist.get(near));
}
});
jt3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
list = new ArrayList<>();
Collections.sort(studentlist);
String key = jt3.getText();
for (int i = 1; i < studentlist.size(); i++) {
if (studentlist.get(i).getnumber().contains(key)) {
list.add(studentlist.get(i));
jt.setText("emmm!你可能是:\n" + list);
//System.out.println("可能的人:" + studentlist.get(i));
//jt.setText("可能的人:" + studentlist.get(i));
}
}
}
});
buttonPanel.add(jButton);
buttonPanel.add(jButton1);
buttonPanel.add(lab);
buttonPanel.add(jt1);
buttonPanel.add(lab1);
buttonPanel.add(jt2);
buttonPanel.add(lab2);
buttonPanel.add(jt3);
buttonPanel.add(jButton2);
add(buttonPanel, BorderLayout.SOUTH);
setSize(DEFAULT_WITH, DEFAULT_HEIGHT);
} public static int agenear(int age) {
int min = 53, value = 0, k = 0;
for (int i = 0; i < studentlist.size(); i++) {
value = studentlist.get(i).getage() - age;
if (value < 0)
value = -value;
if (value < min) {
min = value;
k = i;
}
}
return k;
} }
 package project3;

 import java.awt.*;
import javax.swing.*; public class ButtonTest {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame frame = new Main();
frame.setTitle("身份证");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
}

练习二:

 package project4;

 import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList; import javax.swing.*; public class MyExGUI extends JFrame {
ArrayList<String> user_zongti = new ArrayList<String>();
ArrayList<String> user_zonganswer = new ArrayList<String>();
ArrayList<String> user_answer = new ArrayList<String>();
ArrayList<String> true_answer = new ArrayList<String>();
ArrayList<String> jta_timu = new ArrayList<String>();
ArrayList<String> jta_zong = new ArrayList<String>();
ArrayList<Integer> user_fenshu = new ArrayList<Integer>();
JMenuBar jm; // 菜单条组件
JMenu menu;// 菜单
JMenuItem item1, item2;// 菜单项
JMenu build; // 二级菜单
JMenuItem file, project;
TextArea answer_all = new TextArea();
TextField jta = new TextField();
TextField jta_answer = new TextField();
JLabel num_answer = new JLabel();
JLabel answer;
JToolBar jtb;// 工具条
JButton jb1, jb2, jb3, jb4, jb5, jb6, jb7, jb_next;
int answer_count;
int answer_fenshu; public MyExGUI() {
// 创建菜单
jm = new JMenuBar(); menu = new JMenu("文件(F)");
menu.setMnemonic('f'); // 助记符 build = new JMenu("新建"); file = new JMenuItem("文件");
project = new JMenuItem("答题");
item1 = new JMenuItem("保存(S)");
item2 = new JMenuItem("退出"); answer = new JLabel("第 1 题"); // 添加菜单项至菜单上
build.add(file);
build.add(project); menu.add(build);
menu.add(item1);
menu.add(item2);
menu.addSeparator();
// 将菜单加入至菜单栏
jm.add(menu); JPanel contentPanel = new JPanel();
contentPanel.setLayout(null);
JLabel daan = new JLabel("答案");
JLabel dengyu = new JLabel("=");
num_answer = answer;
num_answer.setFont(new Font("宋体", Font.BOLD, 22));
jb_next = new JButton("下一题");
jta.setFont(new Font("宋体", Font.BOLD, 22));
jta_answer.setFont(new Font("宋体", Font.BOLD, 22));
jb_next.setFont(new Font("宋体", Font.BOLD, 22));
daan.setFont(new Font("宋体", Font.BOLD, 22));
dengyu.setFont(new Font("宋体", Font.BOLD, 22)); contentPanel.add(num_answer);
contentPanel.add(daan);
contentPanel.add(dengyu);
contentPanel.add(jta); contentPanel.add(jta_answer);
contentPanel.add(answer_all);
contentPanel.add(jb_next); num_answer.setBounds(90, 20, 130, 50);
daan.setBounds(250, 20, 90, 50);
jta.setBounds(50, 70, 150, 30);
dengyu.setBounds(205, 70, 20, 20);
jta_answer.setBounds(230, 70, 100, 30);
jb_next.setBounds(350, 70, 110, 30);
answer_all.setBounds(50, 120, 400, 300); this.setJMenuBar(jm); // 添加菜单栏,不能设定位置,会自动放在最上部
this.add(contentPanel); this.setTitle("在线答题系统");
this.setSize(600, 500);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
item1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
FileOutputStream outSTr = null;
BufferedOutputStream Buff = null;
boolean flag = true;
File file;
// String test ;
do {
// test = "test"+count; String inputValue = JOptionPane.showInputDialog("Please input file name");
file = new File(inputValue + "test.txt");
if (!file.exists()) {
// 创建文件
try { flag = file.createNewFile(); } catch (IOException e) {
e.printStackTrace(); }
flag = false;
} else { JOptionPane.showMessageDialog(null, "该文件名已存在,请重新输入", "ERROR", JOptionPane.ERROR_MESSAGE);
flag = true;
}
} while (flag);
// 写入文件
String u_answer;
try {
outSTr = new FileOutputStream(file);
Buff = new BufferedOutputStream(outSTr);
System.out.println("选择是后执行的代码" + user_zongti.size() + user_answer.size());
for (int i = 0; i < user_zongti.size(); i++) {
try {
Buff.write(user_zongti.get(i).getBytes());
Buff.write(" ".getBytes());
u_answer = user_answer.get(i);
if (u_answer.equals(""))
u_answer = "没有作答"; Buff.write(u_answer.getBytes());
Buff.write("\r\n".getBytes());
} catch (IOException e) {
e.printStackTrace();
i--;
}
}
Buff.flush();
Buff.close(); } catch (IOException e) {
e.printStackTrace();
}
try {
outSTr.close();
} catch (IOException e) {
e.printStackTrace();
}
user_zongti.clear();
user_answer.clear();
}
}); project.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
arithmetic art = new arithmetic();
true_answer = art.list_answer;
jta_timu = art.list_timu;
jta_zong = art.list;
answer_count = 1;
answer_all.setText("");
for (int i = 0; i < art.list_timu.size(); i++) {
user_zongti.add(jta_zong.get(i));
answer_all.append(jta_timu.get(i));
answer_all.append("\r\n");
}
num_answer.setText("第 " + answer_count + " 题");
jta.setText(jta_timu.get(answer_count - 1));
answer_count++; }
});
jb_next.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
String temp;
temp = jta_answer.getText(); if (jta.getText().equals("")) {
JOptionPane.showMessageDialog(null, "错误,请导入题库", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
jta_answer.setText("");
if (answer_count <= 10) {
if (isInteger(temp)) { user_answer.add(temp);
System.out.println("选择否后执行的代码" + temp + "user_size" + user_answer.size());
num_answer.setText("第 " + answer_count + " 题");
jta.setText(jta_timu.get(answer_count - 1));
answer_count++;
} else {
JOptionPane.showMessageDialog(null, "错误", "请输入数字", JOptionPane.ERROR_MESSAGE);
}
} else {
user_answer.add(temp);
System.out.println("选择否后执行的代码" + temp + "user_size" + user_answer.size());
answer_fenshu = 0;
for (int i = 0; i < user_answer.size(); i++) {
if (user_answer.get(i).equals(true_answer.get(i)))
answer_fenshu += 5;
}
user_fenshu.add(answer_fenshu);
Object[] options = { "是", "取消" };
int res = JOptionPane.showOptionDialog(null, "是否查看成绩", "答题完毕", JOptionPane.DEFAULT_OPTION,
JOptionPane.YES_NO_OPTION, null, options, options[0]);
if (res == JOptionPane.YES_OPTION) {
chart ct = new chart(user_fenshu);
ct.setVisible(true); } else {
Object[] option = { "是", "取消" };
int res1 = JOptionPane.showOptionDialog(null, "是否退出程序", "终止框", JOptionPane.DEFAULT_OPTION,
JOptionPane.YES_NO_OPTION, null, option, option[0]); if (res1 == JOptionPane.YES_OPTION) {
dispose();
System.exit(0); } else { } } } }
}); item2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dispose();
System.exit(0);
}
}); } public static boolean isInteger(String str) {
for (int i = str.length(); --i >= 0;) {
if (!Character.isDigit(str.charAt(i))) {
return false;
}
}
return true;
} }
 package project4;

 public class Main {

     public static void main(String[] args) {
MyExGUI lg = new MyExGUI();
//new MyExGUI(); } }
 package project4;

 import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner; public class arithmetic {
ArrayList<String> list = new ArrayList<String>();
ArrayList<String> list_timu = new ArrayList<String>();
ArrayList<String> list_answer = new ArrayList<String>(); public arithmetic() {
FileOutputStream outSTr = null;
BufferedOutputStream Buff = null;
int number_n = 10, count; ArrayList<String> list_temp = new ArrayList<String>();
String[] operator = new String[] { "+", "-", "*", "/" }; Random rand = new Random();
File file1 = new File("test.txt");
if (file1.exists()) {
// 创建文件
try {
file1.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
} while (number_n > 0) {
int[] number_temp = new int[rand.nextInt(2) + 3];
String[] str_temp = new String[number_temp.length - 1];
for (int i = 0; i < number_temp.length; i++) {
if (i < number_temp.length - 1) {
number_temp[i] = rand.nextInt(100);
list_temp.add(String.valueOf(number_temp[i]));
str_temp[i] = operator[rand.nextInt(4)];
list_temp.add(str_temp[i]); } else {
number_temp[i] = rand.nextInt(100);
list_temp.add(String.valueOf(number_temp[i]));
}
} count = calculate_RPN(produce_RPN(list_temp));
if (count != -1) {
list_timu.add(transform_string(list_temp));
list_answer.add(String.valueOf(count));
list_temp.add(" = " + count);
list.add(transform_string(list_temp));
number_n--;
list_temp.clear();
} else
list_temp.clear();
System.out.println(number_n); }
try {
outSTr = new FileOutputStream(file1);
Buff = new BufferedOutputStream(outSTr);
for (int i = 0; i < list.size(); i++) {
try {
Buff.write(list.get(i).getBytes());
Buff.write("\r\n".getBytes());
} catch (IOException e) {
e.printStackTrace();
i--;
}
}
Buff.flush();
Buff.close(); } catch (IOException e) {
e.printStackTrace();
}
// Buff.close();
try {
outSTr.close();
} catch (IOException e) {
e.printStackTrace();
} for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i));
System.out.println();
}
System.out.print("计算完毕!"); } public static int calculate_RPN(ArrayList<String> list_temp) {
int i = 0, t;
double a = 0, b = 0;
String l_temp;
Stack sk = new Stack(10);
for (t = 0; t < list_temp.size(); t++) {
l_temp = list_temp.get(i++);
if (!isInteger(l_temp)) {
b = sk.mypop();
a = sk.mypop();
switch (l_temp) {
case "+":
sk.mypush(a + b);
break;
case "-":
if(!(a<b)) {
sk.mypush(a - b);
}
else
return -1;
break;
case "*":
sk.mypush(a * b);
break;
case "/":
if (b == 0||a<b)
return -1;
sk.mypush(a / b);
break;
}
System.out.println("st.mytop: " + sk.mypeek());
} else {
sk.mypush((double) Integer.parseInt(l_temp));
} }
if (!sk.myisempty()) {
a = sk.mypop();
b = a - (int) a;
System.out.println("a: " + a);
if (a > 0 && b == 0) {
return (int) a;
} else
return -1;
} else
return -1; } public static ArrayList<String> produce_RPN(ArrayList<String> list_temp) {
int t = 0, i = 0;
String tmp;
Tack mytack = new Tack(10);
ArrayList<String> lt_temp = new ArrayList<String>();
while (true) {
tmp = list_temp.get(i++);
if (isInteger(tmp)) {
lt_temp.add(tmp);
} else {
if (mytack.myisempty()) {
mytack.mypush(tmp);
} else {
if (isCPriority(tmp, mytack.mypeek()))
mytack.mypush(tmp);
else {
lt_temp.add(mytack.mypop());
mytack.mypush(tmp);
} }
}
if (i >= list_temp.size()) {
while (!mytack.myisempty())
lt_temp.add(mytack.mypop());
System.out.println(transform_string(list_temp));
list_temp = lt_temp;
System.out.println(list_temp);
return list_temp;
}
} } public static boolean isInteger(String str) {
for (int i = str.length(); --i >= 0;) {
if (!Character.isDigit(str.charAt(i))) {
return false;
}
}
return true;
} public static boolean isCPriority(String str, String s) {
if ((str + s).equals("*+") || (str + s).equals("*-") || (str + s).equals("/+") || (str + s).equals("/-"))
return true;
else
return false;
} public static String transform_string(ArrayList<String> list_temp) {
String s = "";
for (int i = 0; i < list_temp.size(); i++) {
s += list_temp.get(i);
}
return s; } static class Stack {
int mytop;
double stk[]; public Stack(int num) {
mytop = -1;
stk = new double[num];
} /* 出栈 */
double mypop() {
double peek = stk[mytop];
mytop--;
return peek;
} /* 入栈 */
void mypush(double x) {
mytop++;
stk[mytop] = x; } /* 判空 */
Boolean myisempty() {
if (mytop == -1)
return true;
else
return false;
} /* 取栈顶元素 */
double mypeek() {
double peek = stk[mytop];
return peek;
} /* 栈大小 */
int mysize() {
return mytop + 1;
}
} static class Tack {
int mytop;
String tk[]; public Tack(int num) {
mytop = -1;
tk = new String[num];
} /* 出栈 */
String mypop() {
String peek = tk[mytop];
mytop--;
return peek;
} /* 入栈 */
void mypush(String x) {
mytop++;
tk[mytop] = x; } /* 判空 */
Boolean myisempty() {
if (mytop == -1)
return true;
else
return false;
} /* 取栈顶元素 */
String mypeek() {
String peek = tk[mytop];
return peek;
} /* 栈大小 */
int mysize() {
return mytop + 1;
} }
}

实验总结:
通过这一周的学习,掌握了线程的概念,线程创建的两种技术,以及多线程运行的原理,理解和掌握了线程的优先级属性及调度方法,线程同步的概念及实现技术。

在编程中布局方面的问题较为困难,展现出来的界面看起来不是很美观,对布局的内容还有待进一步的理解。

201771010126 王燕《面向对象程序设计(Java)》第十六周学习总结的更多相关文章

  1. 201571030332 扎西平措 《面向对象程序设计Java》第八周学习总结

    <面向对象程序设计Java>第八周学习总结   项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https: ...

  2. 201771010118马昕璐《面向对象程序设计java》第八周学习总结

    第一部分:理论知识学习部分 1.接口 在Java程序设计语言中,接口不是类,而是对类的一组需求描述,由常量和一组抽象方法组成.Java为了克服单继承的缺点,Java使用了接口,一个类可以实现一个或多个 ...

  3. 201771010134杨其菊《面向对象程序设计java》第八周学习总结

    第八周学习总结 第一部分:理论知识 一.接口.lambda和内部类:  Comparator与comparable接口: 1.comparable接口的方法是compareTo,只有一个参数:comp ...

  4. 201771010134杨其菊《面向对象程序设计java》第七周学习总结

    第七周学习总结 第一部分:理论知识 1.继承是面向对象程序设计(Object Oriented Programming-OOP)中软件重用的关键技术.继承机制使用已经定义的类作为基础建立新的类定义,新 ...

  5. 201771010128 王玉兰《面象对象程序设计 (Java) 》第六周学习总结

    ---恢复内容开始--- 第一部分:基础知识总结: 1.继承 A:用已有类来构建新类的一种机制,当定义了一个新类继承一个类时,这个新类就继承了这个类的方法和域以适应新的情况: B:特点:具有层次结构. ...

  6. 201871010126 王亚涛《面向对象程序设计 JAVA》 第十三周学习总结

      内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p/ ...

  7. 201871010126 王亚涛 《面向对象程序设计 (Java)》第十七周学习总结

    内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p/12 ...

  8. 201777010217-金云馨《面向对象程序设计Java》第八周学习总结

    项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...

  9. 马凯军201771010116《面向对象程序设计Java》第八周学习总结

    一,理论知识学习部分 6.1.1 接口概念 两种含义:一,Java接口,Java语言中存在的结构,有特定的语法和结构:二,一个类所具有的方法的特征集合,是一种逻辑上的抽象.前者叫做“Java接口”,后 ...

  10. 周强201771010141《面向对象程序设计Java》第八周学习总结

    一.理论知识学习部分 Java为了克服单继承的缺点,Java使用了接口,一个类可以实现一个或多个接口. 接口体中包含常量定义和方法定义,接口中只进行方法的声明,不提供方法的实现. 类似建立类的继承关系 ...

随机推荐

  1. word20161231

    1. measurement 英[ˈmeʒəmənt]美[ˈmeʒərmənt]n. 量度; 份量,尺寸; 测量法; (量得的) 尺寸;[例句]We took lots of measurements ...

  2. jQuery mouse and lunbo

    自动轮播 和 鼠标事件var arr = ["images/d.jpg", "images/q.jpg", "images/c.jpg", ...

  3. .Net业务搭配实用技术栈(转)

      前言 昨天有篇文章在讨论webform的设计思路,我已经四五年不用webform了,虽然它也提供了HttpModule和httphandle来处理请求,提供了一般处理程序ashx来简化处理流程,但 ...

  4. WPF UserControl响应窗体的PreviewKeyDown事件

    目的 在UserControl页面实现通过快捷键打开新建窗口 实现过程 监听Window窗体的PreviewKeyDown 其实,使用KeyDown事件也是可以的 页面代码 <Window x: ...

  5. java程序员技术范围

    1 工具 开发工具.源代码管理.构建工具.测试工具(压力.安全等).接口测试工具.反编译工具.日志工具.第三方工具等 2 java jvm.多线程.socket.io(两种方式).集合(两大接口).异 ...

  6. windows环境安装phantomjs和pyspider遇到的问题

    1. 安装phantomjs 下载地址:http://phantomjs.org/download.html 解压后将phantomjs.exe文件放到python根目录 2.安装pyspider p ...

  7. Sprite子节点透明度不能跟随父节点变化的问题求解(转)

    原出处忘记了. [已解决]Sprite子节点透明度不能跟随父节点变化的问题求解 自己封装了一个按钮控件,点击的时候封装了一些动作,其中有透明度的变化. 当点击发生的时候,Sprite本体执行正常,但是 ...

  8. ASP.NET Core 中使用Session会话

    添加Session Nuget包 更新Startup.cs文件 在ConfigureServices方法中添加如下代码 services.AddSession(options => { // S ...

  9. JAVA 导包,使用前面的类~

    package Code413;/*1.导包,也就是指出需要使用的类,在什么位置import 包名称,类名称对于与当前同类属于同一个包的情况,可以省略导包语句不写..2.创建,格式:类名称 对象名=n ...

  10. OpenStack基础组件安装keystone身份认证服务

    域名解析 vim /etc/hosts 192.168.245.172 controller01 192.168.245.171 controller02 192.168.245.173 contro ...