掷骰子

**多线程&&观察者模式

题目要求:《掷骰子》窗体小游戏,在该游戏中,玩家初始拥有1000的金钱,每次输入押大还是押小,以及下注金额,随机3个骰子的点数,如果3个骰子的总点数小于等于9,则开小,否则开大,然后判断玩家是否押对,如果未押对则扣除下注金额,如果押对则奖励和玩家下注金额相同的金钱。

分析:这个题目要求灵活运用多线程的相关知识,达到点击开始按钮时,有3个线程启动,分别控制3颗骰子的转动,在3颗骰子全部转完以后,回到主线程计算游戏结果。

 //3个线程控制3颗骰子
Thread t1 = new Thread();
Thread t2 = new Thread();
Thread t3 = new Thread();
//启动3个线程
t1.start();
t2.start();
t3.start();
//将3个线程加入主线程
t1.join();
t2.join();
t3.join();

But,,,写完代码以后发现,这样做虽然能够保证游戏能够正确运行,但是当我点击开始按钮时,由于3个骰子线程都是直接开在主线程上的,点击开始按钮时,按钮出现下沉情况,子线程一直在后台运行,我窗体中的图片根本不会发生改变,而是直接显示最后的结果,意思就是骰子一直在后台转动,不在前台的窗体中及时更新显示。后来在网上苦苦找寻,大神们说如果想要通过点击JButton使窗体中的JLabel/JTextFeild等其他组件及时更新,直接在JButton的监听事件的实现方法里面直接创建匿名线程,也就是说直接在actionPerformed()方法中修改代码即可,这样能保证你的组件中内容的及时变换,实现非常炫酷的效果。

代码如下:

public void actionPerformed(ActionEvent e) {

    new Thread(new Runnable() {

        @Override
public void run() { //将外部线程类转移到窗体内部 }
}).start(); }

But,,,But,,,   虽然非常炫酷了,能够实现图片的及时更新了,游戏结果却错了,每次我的骰子还在转动呢,我的游戏结果却早早的就出来了。

原因:3根骰子线程属于子线程,窗体线程属于主线程,问题就在于:子线程可以通过变成精灵线程来保持与主线程的同生死,但是主线程却无法控制子线程何时死亡,只有等待子线程执行完所属的run()方法,结束线程后才知道。

解决方法:在主线程(main)中开3个子线程(t1,t2,t3),在每个子线程上再开一个子子线程(t11,t21,t31)。

t1,t2,t3只运行一次,负责创建子子线程;t11,t21,t31每个线程运行多次,负责控制窗体中的图标及时更新。

这样主线程就不受子线程的影响,开始按钮也不回出现下沉的情况。

但是同样在此处使用join方法也是hold不住子线程的,毕竟t1,t2,t3只运行了一次,join对他们来说根本不起作用,想要掌控t11,t21,t31,最容易理解的办法,就是使用观察者模式了。

将窗体看做观察者,子线程看做被观察者。子线程运行完时,通知观察者我已经运行完成,当观察者观察到子线程全都运行完时,才开始运行后续步骤。

全部代码:

1.窗体

 package com.sxt.dice;

 import java.awt.Color;

 public class DiceFrame extends JFrame implements ActionListener, Observer {

     /**
* 《掷骰子》控制台小游戏,在该游戏中,玩家初始拥有1000的金钱,每次输入押大还是押小,
* 以及下注金额,随机3个骰子的点数,如果3个骰子的总点数小于等于9,则开小,否则开大,
* 然后判断玩家是否押对,如果未押对则扣除下注金额,如果押对则奖励和玩家下注金额相同的金钱。
*
* 运用观察者模式 3个子线程分别控制3个骰子,都已经结束时,通知观察者窗体,窗体观察到所有子线程都结束时,计算游戏结果
*
*/ private static final long serialVersionUID = 1L;
private JTextField txtPut;
private JButton btnStart;
private JLabel labResult;
private JComboBox<String> comboBox;
private JLabel labBigOrSmall;
private JLabel labPut;
private JLabel labSumMoney;
private JLabel labDice3;
private JLabel labDice2;
private JLabel labDice1;
private JLabel labSum;
private JLabel labMes; private static List<Icon> imgs = new ArrayList<Icon>(); public static void main(String[] args) {
new DiceFrame();
} public DiceFrame() {
this.setLocationRelativeTo(null);
this.setBounds(200, 50, 380, 297);
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
getContentPane().setLayout(null);
this.setResizable(false); labDice1 = new JLabel("");
labDice1.setIcon(new ImageIcon("img/dices.jpg"));
labDice1.setBounds(30, 50, 96, 96);
getContentPane().add(labDice1); labSum = new JLabel("\u5269\u4F59\u91D1\u989D\uFF1A");
labSum.setBounds(10, 10, 69, 23);
getContentPane().add(labSum); labDice2 = new JLabel("");
labDice2.setIcon(new ImageIcon("img/dices.jpg"));
labDice2.setBounds(136, 50, 96, 96);
getContentPane().add(labDice2); labDice3 = new JLabel("");
labDice3.setIcon(new ImageIcon("img/dices.jpg"));
labDice3.setBounds(242, 50, 96, 96);
getContentPane().add(labDice3); labSumMoney = new JLabel("3000");
labSumMoney.setForeground(Color.red);
labSumMoney.setBounds(86, 10, 63, 23);
getContentPane().add(labSumMoney); labPut = new JLabel("\u672C\u6B21\u4E0B\u6CE8\uFF1A");
labPut.setToolTipText("0.0");
labPut.setBounds(10, 199, 69, 23);
getContentPane().add(labPut); txtPut = new JTextField();
txtPut.setBounds(80, 200, 69, 21);
getContentPane().add(txtPut);
txtPut.setColumns(10); labBigOrSmall = new JLabel("\u62BC\uFF1A");
labBigOrSmall.setBounds(45, 232, 34, 27);
getContentPane().add(labBigOrSmall); comboBox = new JComboBox<String>();
comboBox.setBounds(80, 234, 69, 23);
getContentPane().add(comboBox);
comboBox.addItem("大");
comboBox.addItem("小"); labResult = new JLabel("");
labResult.setBounds(136, 156, 126, 27);
getContentPane().add(labResult); btnStart = new JButton("START");
btnStart.setBounds(263, 199, 88, 58);
getContentPane().add(btnStart); labMes = new JLabel("<html><font size=5 color=red>*</font></html>");
labMes.setBounds(152, 203, 101, 15);
getContentPane().add(labMes); this.setVisible(true); imgs.add(new ImageIcon("img/1.png"));
imgs.add(new ImageIcon("img/2.png"));
imgs.add(new ImageIcon("img/3.png"));
imgs.add(new ImageIcon("img/4.png"));
imgs.add(new ImageIcon("img/5.png"));
imgs.add(new ImageIcon("img/6.png")); btnStart.addActionListener(this);
} @Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == btnStart) { // 清除上次游戏的结果
labResult.setText(""); // 获取当前下注金额,用户余额,用户押大还是押小
String txt = txtPut.getText().trim();
String remain = labSumMoney.getText().trim(); // 余额不足,不能开始游戏,提示用户充值
if (Integer.parseInt(remain) <= 0) {
JOptionPane.showMessageDialog(null, "当前余额不足,请充值!");
return;
} // 下注金额合法性检查
if (txt.length() == 0) {
// 提示用户输入
labMes.setText("*请输入下注金额");
labMes.setForeground(Color.RED);
return;
}
// 检查用户下注金额是否在有效范围内
if (Integer.parseInt(txt) <= 0
|| Integer.parseInt(txt) > Integer.parseInt(remain)) {
txtPut.setText("");
labMes.setText("下注金额应在0~" + remain + "之间");
return;
} // 游戏开始后相关项不可更改
txtPut.setEnabled(false);
labMes.setText("");
comboBox.setEnabled(false); //在主线程上开t1,t2,t3 3个子线程
Thread t1 = new Thread() {
@Override
public void run() {
//每个子线程上再开子子线程,控制图标变换
IconThread t11 = new IconThread(labDice1, imgs);
//给t11添加观察者,即当前窗体
t11.addObserver(DiceFrame.this);
new Thread(t11).start();
}
}; Thread t2 = new Thread() {
@Override
public void run() {
IconThread t21 = new IconThread(labDice2, imgs);
t21.addObserver(DiceFrame.this);
new Thread(t21).start();
}
}; Thread t3 = new Thread() {
@Override
public void run() {
IconThread t31 = new IconThread(labDice3, imgs);
t31.addObserver(DiceFrame.this);
new Thread(t31).start();
}
}; t1.start();
t2.start();
t3.start();
} } /**
* 获取骰子点数和
*
* @param lab
* @return sum
*/
private int result(JLabel lab) {
// 获取当前骰子图片
Icon icon = lab.getIcon();
int sum = 0;
for (int i = 0; i < imgs.size(); i++) {
if (icon.equals(imgs.get(i))) {
sum += (i + 1);
break;
}
}
return sum;
} // 构建所有被观察者的集合
Vector<Observable> allObservables = new Vector<Observable>(); @Override
public void update(Observable o, Object arg) {
System.out.println(o + ".................");
// 如果集合中不包含当前被观察者,将此被观察者加入集合
if (allObservables.contains(o) == false) {
allObservables.add(o);
} // 如果集合中被观察者个数为3,说明3个骰子线程已经全部结束
if (allObservables.size() == 3) {
// 获取当前下注金额,用户余额,用户押大还是押小
String txt = txtPut.getText().trim();
String remain = labSumMoney.getText().trim();
String bigOrSmall = comboBox.getSelectedItem().toString();
// 获取每个骰子点数
int sum1 = result(labDice1);
int sum2 = result(labDice2);
int sum3 = result(labDice3);
System.out.println(sum1 + "-" + sum2 + "-" + sum3);
int sum = sum1 + sum2 + sum3;
System.out.println(sum); if (sum > 9 && "大".equals(bigOrSmall) || sum <= 9
&& "小".equals(bigOrSmall)) { // 奖励玩家相应金额
remain = String.valueOf(Integer.parseInt(remain)
+ Integer.parseInt(txt));
labSumMoney.setText(remain); // 显示游戏结果
labResult.setText("WIN");
labResult.setForeground(Color.GREEN);
labResult.setFont(new Font("宋体", Font.BOLD, 40)); } else {
// 扣除玩家相应金额
remain = String.valueOf(Integer.parseInt(remain)
- Integer.parseInt(txt));
labSumMoney.setText(remain); labResult.setText("FAIL");
labResult.setForeground(Color.red);
labResult.setFont(new Font("宋体", Font.BOLD, 40)); }
txtPut.setEnabled(true);
comboBox.setEnabled(true);
// 本次游戏结束后移除集合中所有线程
allObservables.removeAll(allObservables);
}
} }

2.线程

 package com.sxt.dice;

 import java.util.List;
import java.util.Observable;
import java.util.Random; import javax.swing.Icon;
import javax.swing.JLabel; public class IconThread extends Observable implements Runnable {
/**
* 运用观察者模式,将子线程作为被观察对象,一旦子线程运行完,发生改变,通知观察者
*/
JLabel lab; Random random = new Random();
List<Icon> imgs; public IconThread(JLabel lab, List<Icon> imgs) {
this.lab = lab;
this.imgs = imgs; } @Override
public void run() {
//设置每颗骰子转动30次
int count = 30;
while (count > 0) { //获取一个随机数[0~6)
int index = random.nextInt(6);
//从imgs集合中取相应图片放入lab中
lab.setIcon(imgs.get(index));
count--; try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} this.setChanged();// 子线程运行完,发生改变
this.notifyObservers();// 通知观察者
}
}

掷骰子游戏窗体实现--Java初级小项目的更多相关文章

  1. 迷你图书管理系统 源代码 Java初级小项目

    今天博主再给大家分享一个小项目:MiNi图书管理系统.用的是Java语言开发的,代码不多,大概260行左右吧,系统是实现图书的新增图书.删除图书.借阅图书.归还图书.查看图书等简单的功能(后附源代码) ...

  2. 吃货联盟订餐系统 源代码 Java初级小项目

    咳咳,今天博主给大家写一个小的项目:吃货联盟订餐系统.博主不是大神(互联网架构师的路上ing),也是小白一个,不过是刚入门的小白^_^.项目功能也很简单:只是模拟日常的订餐流程呦,所以有错误以及功能不 ...

  3. 小项目,吃货联盟,java初级小项目,源代码

    1:项目的实现效果.功能如图所示. 2:项目的源代码如下: import java.util.Scanner; /** * 吃货联盟订餐管理系统 * */ public class OrderingM ...

  4. 嗖嗖移动大厅 源代码 Java初级小项目

    今天给大家一个比较综合的项目:嗖嗖移动业务大厅.项目功能很多,概括的功能也很全面.吃透了这个项目,你的java基础部分已经非常棒了!!! 一 . 项目概述 技能要求  使用面向对象设计的思想  合 ...

  5. python学习之掷骰子游戏

    """ 通过学习的python知识,写一个简单的python小游戏 游戏名字:掷骰子比大小 游戏规则: 1.玩家可以选择玩掷几个骰子游戏(默认3个) 2.玩家可以设置双方 ...

  6. java初学小项目-酒店客房管理系统

    最近初次接触JAVA,感觉之前学的C语言很有用,跟着视频做了一个小项目-酒店客房管理系统 /* 酒店客房管理系统 */ import java.util.Scanner;//通过键盘来输入命令需要的引 ...

  7. Java数据库小项目02--管家婆项目

    目录 项目要求 开发环境搭建 工具类JDBCUtils 创建管家婆数据表 项目分层 MainApp层 MainView层 ZhangWuController层 ZhangWuService层 Zhan ...

  8. java连连看小项目

    /* *本人也是刚入门,希望各位多多指教 *该项目主要代码在于连线 *1.2个连线没有拐弯 *2.2个连线有一个拐弯 *3.2个连线有2个拐弯 *采用递归算法 */ package llk; impo ...

  9. java基础小项目练习之1----3天做出飞机大战

    Shoot射击游戏第一天一. 关键问题(理论):1. 简述FlyingObject.Enemy.Award.Airplane.Bee.Bullet.Hero之间的继承与实现关系2. 简述Hero类构造 ...

随机推荐

  1. 001-Servlet模板

    package ${enclosing_package}; import java.io.IOException; import javax.servlet.ServletException; imp ...

  2. CentOS 6.4下安装JIRA6.3.6破解汉化

    JIRA产品非常完善且功能强大,安装配置简单,多语言支持.界面十分友好,和其他系统如CVS.Subversion(SVN).VSS.LDAP.邮件服务整合得相当好,文档齐全,可用性以及可扩展性方面都十 ...

  3. Java 中 String 的构造方法

    String 对于所有 Java 程序员来说都不会陌生,几乎每天甚至每个程序都会和 String 打交道,因此将 String 的常用知识汇集在此,方便查阅. 概叙: Java 中是如此定义 Stri ...

  4. [中英对照]The sysfs Filesystem | sysfs文件系统

    The sysfs Filesystem | sysfs文件系统 Abstract | 摘要 sysfs is a feature of the Linux 2.6 kernel that allow ...

  5. Python(3):文件读写与异常

    访问路径: 文件读写必然涉及到文件会放在某个路径下.在python里,可以通过引入os包来实现切换当前访问的路径: # 假设我在 /home/zyq/KiDe/Python/test 文件夹中有一个文 ...

  6. JQuery语法 JQuery对象与原生对象互转 文档就绪函数与window.onload的区别

    [JQuery语法] 1.jQuery("选择器").action();通过选择器调用事件函数,但是jquery中,jquery可以用$(“选择器”).action();   ① ...

  7. 使用 csc.exe 编译C#代码

    csc.exe是C#的编译器,可以将C#代码编译为IL中间语言代码(exe.dll),然后再通过.net framework中的clr内的JIT(即时编译器)将中间语言代码编译为机器语言,然后再由机器 ...

  8. thinkphp htmlspecialchars_decode

    一  百度编辑器 与 htmlspecialchars_decode *Thinkphp百度编辑器 存的时候为了安全把进行了字符转换,数据库: <p>&nbsp;测试测试</ ...

  9. Web安全相关(四):过多发布(Over Posting)

    简介 过多发布的内容相对比较简单,因此,我只打算把原文中的一些关键信息翻译一下.原文链接如下: http://www.asp.net/mvc/overview/getting-started/gett ...

  10. Cookie介绍

    1.Http协议与Cookie Cookie(小量信息)是HTTP协议指定的!先由服务器保存Cookie到浏览器,而下次浏览器请求服务器时把上一次请求得到Cookie再归还给服务器 由服务器创建保存到 ...