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. SyntaxError: Non-ASCII character '\xe4' in file t.py on line 3, but no encoding declared

    问题 报错代码 #!/usr/bin/python s = "你好" print s 执行报错: File "t.py", line 3 SyntaxError ...

  2. ionic platform add ios, Error:spawn EACCES

    RT: cordova ionic 环境搭建好之后,需要添加平台才能打包,添加平台如果出错:Error:spawn EACCES, 原因是因为没添加hooks, 请使用 ionic add hooks ...

  3. sublime text2+Ctags+Cscope替代Source Insight

    说明:以Windows系统下查看C++代码为例.因为Source Insight(以下简称SI)是收费软件,且界面丑陋,所以考虑其替代方案,发现Sublime Text3(以下简称ST3) + Cta ...

  4. WebApi安全性 使用TOKEN+签名验证 (秘钥是GUID的,私有的,不是雙方的,并不在网络连接上传输)

    转http://www.cnblogs.com/MR-YY/archive/2016/10/18/5972380.html WebApi安全性 使用TOKEN+签名验证   首先问大家一个问题,你在写 ...

  5. nodejs在后台运行

    安装forever npm install forever -g 使用 forever 启动 app forever start app.js 启动app并输出日志 forever start -l ...

  6. centos su命令

    有很多指令都只可以用 root 身份去执行,因此我们需要成为 root 用户.要这样做,我们可以使用 su 指令(更替用户).su 指令有下列格式: su - <user>或su < ...

  7. 关于sql 注入,感觉比较全的一篇文章

    原文链接 http://netsecurity.51cto.com/art/201705/538863.htm

  8. HTTP之Cookie

    cookie是什么 浏览器存储在本地电脑上的一小段文本文件,cookie的存在主要是为了解决http协议无状态的问题,例如通过cookie来判断用户的登录状态,是否是某一个用户等. cookie的结构 ...

  9. python3 urllib模块使用

    urllib模块使用 urllib.request urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=N ...

  10. react事件中的事件对象和常见事件

    不管是在原生的js还是vue中,所有的事件都有其事件对象,该事件对象event中包含着所有与事件相关的信息,在react中,所有的事件也有其事件对象,在触发DOM上的某个事件时,就会产生一个事件对象. ...