介绍完如何创建进程以及线程了,那么我们接着来看一个实例:

  利用多线程模拟 3 个窗口卖票

第一种方法:继承 Thread 类

 创建窗口类 TicketSell 

package com.ys.thread;

public class TicketSell extends Thread{
//定义一共有 50 张票,注意声明为 static,表示几个窗口共享
private static int num = 50; //调用父类构造方法,给线程命名
public TicketSell(String string) {
super(string);
}
@Override
public void run() {
//票分 50 次卖完
for(int i = 0 ; i < 50 ;i ++){
if(num > 0){
try {
sleep(10);//模拟卖票需要一定的时间
} catch (InterruptedException e) {
// 由于父类的 run()方法没有抛出任何异常,根据继承的原则,子类抛出的异常不能大于父类, 故我们这里也不能抛出异常
e.printStackTrace();
}
System.out.println(this.currentThread().getName()+"卖出一张票,剩余"+(--num)+"张");
}
}
} }

  创建主线程测试:

package com.ys.thread;

public class TestTicket {

	public static void main(String[] args) {
//创建 3 个窗口
TicketSell t1 = new TicketSell("A窗口");
TicketSell t2 = new TicketSell("B窗口");
TicketSell t3 = new TicketSell("C窗口"); //启动 3 个窗口进行买票
t1.start();
t2.start();
t3.start();
}
}

  结果:这里我们省略了一些,根据电脑配置,结果会随机出现不同

B窗口卖出一张票,剩余48张
A窗口卖出一张票,剩余47张
C窗口卖出一张票,剩余49张
C窗口卖出一张票,剩余46张
B窗口卖出一张票,剩余44张
A窗口卖出一张票,剩余45张
A窗口卖出一张票,剩余43张
...
C窗口卖出一张票,剩余5张
A窗口卖出一张票,剩余4张
B窗口卖出一张票,剩余3张
A窗口卖出一张票,剩余2张
C窗口卖出一张票,剩余3张
B窗口卖出一张票,剩余1张
C窗口卖出一张票,剩余0张
A窗口卖出一张票,剩余-1张

第二种方法:实现 Runnable 接口

  创建窗口类 TicketSellRunnable

package com.ys.thread;

public class TicketSellRunnable implements Runnable{

	//定义一共有 50 张票,继承机制开启线程,资源是共享的,所以不用加 static
private int num = 50; @Override
public void run() {
//票分 50 次卖完
for(int i = 0 ; i < 50 ;i ++){
if(num > 0){
try {
//模拟卖一次票所需时间
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+(--num)+"张");
}
}
} }

  创建主线程测试:

package com.ys.thread;

public class TicketSellRunnableTest {
public static void main(String[] args) {
TicketSellRunnable t = new TicketSellRunnable(); Thread t1 = new Thread(t,"A窗口");
Thread t2 = new Thread(t,"B窗口");
Thread t3 = new Thread(t,"C窗口"); t1.start();
t2.start();
t3.start();
} }

  结果:同理为了篇幅我们也省略了中间的一些结果

B窗口卖出一张票,剩余49张
C窗口卖出一张票,剩余48张
A窗口卖出一张票,剩余49张
B窗口卖出一张票,剩余47张
A窗口卖出一张票,剩余45张
......
A窗口卖出一张票,剩余4张
C窗口卖出一张票,剩余5张
A窗口卖出一张票,剩余3张
B窗口卖出一张票,剩余2张
C窗口卖出一张票,剩余1张
B窗口卖出一张票,剩余0张
A窗口卖出一张票,剩余-2张
C窗口卖出一张票,剩余-1张

  

结果分析:这里出现了票数为 负数的情况,这在现实生活中肯定是不存在的,那么为什么会出现这样的情况呢?

  

解决办法分析:即我们不能同时让超过两个以上的线程进入到 if(num>0)的代码块中,不然就会出现上述的错误。我们可以通过以下三个办法来解决:

1、使用 同步代码块

2、使用 同步方法

3、使用 锁机制

①、使用同步代码块

语法:
synchronized (同步锁) {
//需要同步操作的代码
} 同步锁:为了保证每个线程都能正常的执行原子操作,Java 线程引进了同步机制;同步锁也叫同步监听对象、同步监听器、互斥锁;
Java程序运行使用的任何对象都可以作为同步监听对象,但是一般我们把当前并发访问的共同资源作为同步监听对象 注意:同步锁一定要保证是确定的,不能相对于线程是变化的对象;任何时候,最多允许一个线程拿到同步锁,谁拿到锁谁进入代码块,而其他的线程只能在外面等着

  实例:

public void run() {
//票分 50 次卖完
for(int i = 0 ; i < 50 ;i ++){
//这里我们使用当前对象的字节码对象作为同步锁
synchronized (this.getClass()) {
if(num > 0){
try {
//模拟卖一次票所需时间
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+(--num)+"张");
}
} }
}

②、使用 同步方法

语法:即用  synchronized  关键字修饰方法

@Override
public void run() {
//票分 50 次卖完
for(int i = 0 ; i < 50 ;i ++){
sell(); }
}
private synchronized void sell(){
if(num > 0){
try {
//模拟卖一次票所需时间
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+(--num)+"张");
}
}

  注意:不能直接用 synchronized 来修饰 run() 方法,因为如果这样做,那么就会总是第一个线程进入其中,而这个线程执行完所有操作,即卖完所有票了才会出来。

③、使用 锁机制

public interface Lock

  主要方法:

  常用实现类:

public class ReentrantLock
extends Object
implements Lock, Serializable
//一个可重入互斥Lock具有与使用synchronized方法和语句访问的隐式监视锁相同的基本行为和语义,但具有扩展功能。

  例子:

package com.ys.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class TicketSellRunnable implements Runnable{ //定义一共有 50 张票,继承机制开启线程,资源是共享的,所以不用加 static
private int num = 50;
//创建一个锁对象
Lock l = new ReentrantLock(); @Override
public void run() {
//票分 50 次卖完
for(int i = 0 ; i < 50 ;i ++){
//获取锁
l.lock();
try {
if(num > 0){
//模拟卖一次票所需时间
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+(--num)+"张");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//释放锁
l.unlock();
} }
}
private void sell(){ } }

Java 多线程详解(三)------线程的同步的更多相关文章

  1. java多线程详解(3)-线程的互斥与同步

    前言:前一篇文章主要描述了多线程中访成员变量与局部变量问题,我们知道访成员变量有线程安全问题,在多线程程序中 我们可以通过使用synchronized关键字完成线程的同步,能够解决部分线程安全问题 在 ...

  2. java多线程详解(6)-线程间的通信wait及notify方法

    Java多线程间的通信 本文提纲 一. 线程的几种状态 二. 线程间的相互作用 三.实例代码分析 一. 线程的几种状态 线程有四种状态,任何一个线程肯定处于这四种状态中的一种:(1). 产生(New) ...

  3. java多线程详解(7)-线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, 这样频繁创建线程就会大大降低系 ...

  4. Java多线程详解(三)

    1)死锁 两个线程相互等待对方释放同步监视器时会出现死锁的现象,这时所有的线程都处于阻塞状态,程序无法继续向下执行. 如下就是会出现死锁的程序. 首先flag = 1,线程d1开始执行,锁住对象o1, ...

  5. Java 多线程详解(四)------生产者和消费者

    Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html Java 多线程详解(二)------如何创建进程和线程: ...

  6. Java多线程详解(二)

    评论区留下邮箱可获得<Java多线程设计模式详解> 转载请指明来源 1)后台线程 后台线程是为其他线程服务的一种线程,像JVM的垃圾回收线程就是一种后台线程.后台线程总是等到非后台线程死亡 ...

  7. Java多线程详解

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  8. java 多线程详解

    一.重点 重点: 1.创建和启动线程 2.实现线程调度 3.实现线程同步 4.实现线程通信 1.为什么要学习多线程? 当多个人访问电脑上同一资源的时候,要用到多线程,让每个人感觉很多电脑同时为多个人服 ...

  9. 干货:Java多线程详解(内附源码)

      线程是程序执行的最小单元,多线程是指程序同一时间可以有多个执行单元运行(这个与你的CPU核心有关). 在java中开启一个新线程非常简单,创建一个Thread对象,然后调用它的start方法,一个 ...

  10. 原创Java多线程详解(一)

    只看书不实践是不行的.来实践一下~~~~~~(引用请指明来源) 先看看百科对多线程的介绍 多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的 ...

随机推荐

  1. Object-C定时器,封装GCD定时器的必要性!!! (一)

    实际项目开发中经常会遇到延迟某件任务的执行,或者让某件任务周期性的执行.然后也会在某些时候需要取消掉之前延迟执行的任务. iOS中延迟操作有三种解决方案: 1.NSObject的方法:(对象方法) p ...

  2. jquery treeTable插件使用细则

    简介 treeTable是跨浏览器.性能很高的jquery的树表组件,它使用非常简单,只需要引用jquery库和一个js文件,接口也很简单. 优点 兼容主流浏览器: 支持IE6和IE6+, Firef ...

  3. 使用原生 JavaScript 操作 DOM

    原文:https://www.sitepoint.com/dom-manipulation-vanilla-javascript-no-jquery/ 微软官方放弃了 IE10-,所以现在可以放心使用 ...

  4. VS 2017开发ASP.NET Core Web应用过程中发现的一个重大Bug

    今天试着用VS 2017去开发一个.net core项目,想着看看.net core的开发和MVC5开发有什么区别,然后从中发现了一个VS2017的Bug. 首先,我们新建项目,ASP.NET Cor ...

  5. 不忘初心 --- 重读<<The C Programming Language>>

    这篇文章应该发布在好几年前,2011年计算机界大师Dennis Ritchie仙逝,那时对大师的映象还停留在大一刚学编程时:Unix的合作开发者,C语言的发明人.通过网上的纪念文章<<Un ...

  6. Node.js 安装配置介绍

    Node.js 安装配置 本章节我们将向大家介绍在window和Linux上安装Node.js的方法. 本安装教程以Node.js v6.10.1 LTS(长期支持版本)版本为例. Node.js安装 ...

  7. java中GUI的awt和Swing的知识点

    刚刚学习了java的GUI,写了几个程序,基本熟悉了awt和Swing,下面和大家分享一下知识点 1.JFrame的层次结构 参考:http://tieba.baidu.com/p/200421612 ...

  8. 学习面向对象编程OOP 第一天

    面向对象编程 Object Oriented Programming 一.什么是面向对象编程OOP 1.计算机编程架构; 2.计算机程序是由一个能够起到子程序作用的单元或者对象组合而成.也就是说由多个 ...

  9. 使用swagger实现web api在线接口文档

    一.前言 通常我们的项目会包含许多对外的接口,这些接口都需要文档化,标准的接口描述文档需要描述接口的地址.参数.返回值.备注等等:像我们以前的做法是写在word/excel,通常是按模块划分,例如一个 ...

  10. 【BFS + Hash】拼图——携程2017春招编程题2

    写在前面 前天参加了携程的网测--还是感觉自己太!渣!了!    _(:з」∠)_ 时光匆匆啊,已经到了开始思考人生的时候了(算了不矫情了)--总之写个博客来督促一下自己.之前太懒了,很多时候都是输在 ...