1.多线程  2.卖票  


1.多线程实现

两种方式可以实现多线程:

  • 继承 Thread 类,重写 run 方法;定义对象,调用 start 方法
  • 创建类实现 Runnable 接口,作为实参传递给 thread 的构造方法。定义对象,调用 start 方法。

1.1.继承 Thread

  • 继承类,重写方法
class TDemo1 extends Thread {
public String name; // 取个名字,便于识别 public TDemo1 (String name) { // 构造方法
this.name = name;
} @Override
public void run() { // 重写 run 方法
show();
} public void show() {
System.out.println(name + ": talk show time");
}
}
  • 创建对象,调用 start 方法启动线程
TDemo1 td1 = new TDemo1("梁宏达");
TDemo1 td2 = new TDemo1("李晨伟");
TDemo1 td3 = new TDemo1("窦文涛");
TDemo1 td4 = new TDemo1("备胎说车"); td1.start();
td2.start();
td3.start();
td4.start();

1.2.实现 Runnable

  • 实现接口
class TDemo2 implements Runnable {
public String name; // 识别 public TDemo2(String name) { // 构造方法
this.name = name;
} @Override
public void run() { // 实现方法
show();
} private void show() {
System.out.println(name + " 新媒体开播");
} }
  • 创建对象,调用方法 start
Thread td1 = new Thread(new TDemo2("备胎说车"));
Thread td2 = new Thread(new TDemo2("30秒懂车"));
Thread td3 = new Thread(new TDemo2("汽车洋葱圈"));
Thread td4 = new Thread(new TDemo2("根叔说车")); td1.start();
td2.start();
td3.start();
td4.start();

2.卖票

2.1.多线程的弊端

  • 实现类
public class Piao implements Runnable {
public static int ticketCount = 5; // 总票数
public int tmpCount = 0; // 售票数量
public String tNum; // 窗口编号、查询窗口radio public Piao(String tNum) { // 构造方法
this.tNum = tNum;
} @Override // 方法重写
public void run() {
if (!"radio".equals(this.tNum)){
sell();
} else {
try {
radio();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} // 查询台,只查询剩余票数,不卖票
private void radio() throws InterruptedException {
int n =0;
while (true) {
System.out.println("剩余票数: " + ticketCount);
Thread.sleep(100);
if(n>3) {
break;
}
n++;
}
} // 售票窗口
private void sell() {
while (true) { // 窗口不打烊
// 车票数量统一管理
if (ticketCount > 0) {
System.out.println("售票窗口 " + tNum + "销售车票" + (++tmpCount) + "张");
ticketCount--;
}
}
}
}
  • 主方法
public class TicketDemo1 {

    public static void main(String[] args) {

        Thread t1 = new Thread(new Piao("001"));
Thread t2 = new Thread(new Piao("002"));
Thread t3 = new Thread(new Piao("003"));
Thread t4 = new Thread(new Piao("004")); // 售票窗口
Thread t0 = new Thread(new Piao("radio")); // 查询窗口 t1.start();
t2.start();
t3.start();
t4.start();
t0.start();
}
}
  • 运行结果:每次运行都不尽相同,出问题的时候多有发生。
售票窗口 003销售车票1张
售票窗口 003销售车票2张
售票窗口 003销售车票3张
售票窗口 003销售车票4张
售票窗口 002销售车票1张
售票窗口 001销售车票1张
售票窗口 004销售车票1张
售票窗口 003销售车票5张
剩余票数: 2
剩余票数: -3
剩余票数: -3
剩余票数: -3
剩余票数: -3

同时开通了 四 个售票窗口,一 个查询窗口,四 个售票窗口一共销售了 八 张票。而实际总票数为 五 张。程序运行出错了

2.2.多线程

  • 出现问题的原因:

    • 多线程同时运行
    • 有些数据被多线程共享
    • 线程会修改共享的数据
  • 解决的方法:在共享数据的读取处理,加锁
  • 加锁须被线程共享

2.3.多线程加锁

  • 加锁(对象锁):使用同步代码块,须事先定义一个 静态锁对象。 使用了 “同一个锁对象圈定的代码块” 的线程,共享锁。
public class Piao2 implements Runnable {
public static int ticketCount = 50; // 总票数
static Object obj = new Object(); // 锁对象 // 多线程重写方法
@Override
public void run() {
sell();
} // 售票员售票方法
private void sell() {
while (true) { // 售票窗口不打烊
synchronized (obj) { // 锁子放在循环内
// 车票数量统一管理
if (ticketCount > 0) {
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
e.printStackTrace();
} ticketCount--;
System.out.println(Thread.currentThread().getName() + " 窗口,剩余车票" + ticketCount + "张。");
}
}
}
}
}

运行后,发现偶尔还是会多卖。说明还是没有锁住。修改 锁对象定义,多个线程不能共享锁。把锁定义为 static。

    static Object obj = new Object(); // 锁对象
  • 加锁(方法锁):为一个静态方法加锁,使得调用方法的对象亦可 共享锁。
public class Piao3 implements Runnable {
public static int ticketCount = 100; // 总票数 // 多线程重写方法
@Override
public void run() {
try {
sell();
} catch (InterruptedException e) {
e.printStackTrace();
}
} // 售票员售票方法
private static synchronized void sell() throws InterruptedException {
while (true) { // 窗口不打烊
if (ticketCount > 0) {
System.out.println(Thread.currentThread().getName() + "售票窗口" + "查询 -> 剩余票数" + ticketCount );
ticketCount--;
}
}
}
}

  加锁后,确实没有 出现多卖票的情况。但是有个马达,就是只要哪个窗口卖出了第一张票,就一卖到底,其他窗口没有票卖。

  • 原因是:俩原因

    • 静态方法,静态方法加锁所有对象共享该锁
    • 加锁后,进入循环,谁调用方法早,当然就把票往空了卖(比黄牛还黄)。
  • 解决一个窗口把票往光了卖的情况,分析:去掉静态,不可行,因为去掉静态后加锁失去效果。每个对象自己加自己的锁……失去加锁意义,

  调整:继续使用静态锁。把 synchronized 限定放到循环里边,每一次循环都加锁,每次循环都同时加锁、解锁。解锁后就可以继续竞争。

// 售票员售票方法
private void sell() {
while (true) { // 窗口不打烊
sell(1);
}
} private static synchronized void sell(int n) {
if (ticketCount > 0) {
System.out.println(Thread.currentThread().getName() + "售票窗口" + "查询 -> 剩余票数" + ticketCount);
ticketCount--;
}
}

静态方法加锁,保证了多个对象共享一个锁。锁放在循环外,保证每个窗口都有票可卖。

2.4.锁对象

  在 2.3 中,对象锁有一个明确定义的静态对象,而方法锁中只能看到方法须是静态的。非静态方法,锁对象是 this;静态方法的锁对象是该类的字节码对象。由于字节码对象,整个类、类对象共享,因此锁能限定线程;而使用了非静态方法,就无法锁定。

Java 多线程 (Thread 类)的更多相关文章

  1. 探Java多线程Thread类和Runnable接口之间的联系

    首先复习一下Java多线程实现机制,Java实现多线程方法有如下这么几种: 1.继承了(extends)Thread类 2.实现了(implements)Runnable接口 也就是说  有如下两种情 ...

  2. Java多线程Thread类了解和使用

    创建线程的两种方式 extends Thread 类 public class WelComeApp { public static void main(String[] args) { Welcom ...

  3. Java并发--Thread类详情

    以下是本文的目录大纲: 一.线程的状态 二.上下文切换 三.Thread类中的方法 转载原文链接:http://www.cnblogs.com/dolphin0520/p/3920357.html 一 ...

  4. Java 线程--继承java.lang.Thread类实现线程

    现实生活中的很多事情是同时进行的,Java中为了模拟这种状态,引入了线程机制.先来看线程的基本概念. 线程是指进程中的一个执行场景,也就是执行流程,进程和线程的区别: 1.每个进程是一个应用程序,都有 ...

  5. java.lang.Thread类详解

    java.lang.Thread类详解 一.前言 位于java.lang包下的Thread类是非常重要的线程类,它实现了Runnable接口,今天我们来学习一下Thread类,在学习Thread类之前 ...

  6. Java多线程——ThreadLocal类的原理和使用

    Java多线程——ThreadLocal类的原理和使用 摘要:本文主要学习了ThreadLocal类的原理和使用. 概述 是什么 ThreadLocal可以用来维护一个变量,提供了一个ThreadLo ...

  7. 2.匿名类,匿名类对象,private/protected/public关键字、abstract抽象类,抽象方法、final关键字的使用,多线程Thread类start方法原理

    package com.bawei.multithread; //注意:模板方法我们通常使用抽象类或者抽象方法!这里我们为了方便在本类中使用就没有使用抽象类/抽象方法 public class Tem ...

  8. java多线程系类:JUC线程池:02之线程池原理(一)

    在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...

  9. java多线程系类:JUC线程池:01之线程池架构

    概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容--线程池.内容包括:线程池架构 ...

  10. java多线程系类:JUC锁:01之框架

    本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--"JUC锁"01之 框架02. Java多线程系列--"JUC锁&q ...

随机推荐

  1. 自然语言处理工具python调用hanlp中文实体识别

    Hanlp作为一款重要的中文分词工具,在GitHub的用户量已经非常之高,应该可以看得出来大家对于hanlp这款分词工具还是很认可的.本篇继续分享一篇关于hanlp的使用实例即Python调用hanl ...

  2. RedHat6.5安装Spark单机

    版本号: RedHat6.5   RHEL 6.5系统安装配置图解教程(rhel-server-6.5) JDK1.8      http://blog.csdn.net/chongxin1/arti ...

  3. 并行计算——理想的PRAM模型

    PRAM(Parallel Random Access Machine)模型是多指令流多数据流(MIMD)并行机中的一种具有共享存储的模型.它假设有一个无限大容量的共享存储器,并且有多个功能相同的处理 ...

  4. 关于PHP程序员技术职业生涯规划 转自 韩天锋

    转自 http://rango.swoole.com/ 看到很多PHP程序员职业规划的文章,都是直接上来就提Linux.PHP.MySQL.Nginx.Redis.Memcache.jQuery这些, ...

  5. Python 创建XML

    https://blog.csdn.net/seetheworld518/article/details/49535285

  6. 【web】之获取服务器tomcat下webapps目录路径

    String realPath = req.getServletContext().getRealPath("/"); String realPathParent=(new Fil ...

  7. mongoDB oplog的说明及应用

    mongoDB oplog 说明 ts:8字节的时间戳,由4字节unix timestamp + 4字节自增计数表示.这个值很重要,在选举(如master宕机时)新primary时,会选择ts最大的那 ...

  8. JS之事件机制

    一.绑定事件的3种方式 1.内联绑定事件 2.on+事件名,绑定事件程序 3.通过addEventListener/removeEventListener绑定事件 不支持ie9之前的浏览器,ie9之前 ...

  9. uoj#29. 【IOI2014】Holiday

    http://uoj.ac/problem/29 经过的点集一定是一个包含start的区间,为了经过这个区间内所有点,必须先到达一个区间端点,再到达另一个区间端点,剩余的步数则贪心选区间内最大价值的点 ...

  10. psql: could not connect to server: No such file or directory&&PGHOST

    由于环境变量 PGHOST配置不当引起的 postgres@pgdb-> psql psql: could not connect to server: No such file or dire ...