一,前言

  单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明。请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式进行探索的。

  本文的目的是:结合文章中的八种单例模式的写法,使用实际的示例,来演示线程安全和效率

  既然是实际的示例,那么就首先定义一个业务场景:购票。大家都知道在春运的时候,抢票是非常激烈的。有可能同一张票就同时又成百上千的人同时在抢。这就对代码逻辑的要求很高了,即不能把同一张票多次出售,也不能出现票号相同的票。

  那么,接下来我们就使用单例模式,实现票号的生成。同时呢在这个过程中利用上述文章中的八种单例模式的写法,来实践这八种单例模式的线程安全性和比较八种单例模式的效率。

  既然文章中第三种单例模式(懒汉式)是线程不安全的,那么我就从这个单例模式的实现开始探索一下线程安全。

  因为不管是八种单例模式的实现方式的哪一种,票号的生成逻辑都是一样的,所以,在此正式开始之前,为了更方便的编写示例代码,先做一些准备工作:封装票号生成父类代码。

二,封装票号生成父类代码

package com.zcz.singleton;

public class TicketNumberHandler {
//记录下一个唯一的号码
private long nextUniqueNumber = 1;
/**
* 返回生成的号码
* @return
*/
public Long getTicketNumber() {
return nextUniqueNumber++;
}
}

  票号的生成逻辑很简单,就是一个递增的整数,每获取一次,就增加1。以后我们的每一种单例模式都继承这个父类,就不用每一次都编写这部分代码,做到了代码的重用。

  接下来就是实现第三种单例模式,探索一下会不会引起线程安全问题。

三,实现第三种单例模式

package com.zcz.singleton;

/**
* 票号生成类——单利模式,即整个系统中只有唯一的一个实例
* @author zhangchengzi
*
*/
public class TicketNumberHandler3 extends TicketNumberHandler{
//保存单例实例对象
private static TicketNumberHandler3 INSTANCE;
//私有化构造方法
private TicketNumberHandler3() {}; /**
* 懒汉式,在第一次获取单例对象的时候初始化对象
* @return
*/
public static TicketNumberHandler3 getInsatance() {
if(INSTANCE == null) {
try {
//这里为什么要让当前线程睡眠1毫秒呢?
//因为在正常的业务逻辑中,单利模式的类不可能这么简单,所以实例化时间会多一些
//让当前线程睡眠1毫秒
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
INSTANCE = new TicketNumberHandler3();
}
return INSTANCE;
}
}

  代码与上述文章的一模一样,那么接下来就开始编写测试代码。

四,编写测试代码

package com.zcz.singleton;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.Vector; public class BuyTicket {
public static void main(String[] args) {
// 用户人数
int userNumber = 10000;
// 保存用户线程
Set<Thread> threadSet = new HashSet(); // 用于存放TicketNumberHandler实例对象
List<TicketNumberHandler> hanlderList = new Vector();
// 保存生成的票号
List<Long> ticketNumberList = new Vector(); // 定义购票线程,一个线程模拟一个用户
for(int i=0;i<userNumber;i++) {
Thread t = new Thread() {
public void run() {
TicketNumberHandler handler = TicketNumberHandler3.getInsatance();
hanlderList.add(handler); Long ticketNumber = handler.getTicketNumber();
ticketNumberList.add(ticketNumber);
};
};
threadSet.add(t);
}
System.out.println("当前购票人数:"+threadSet.size()+" 人"); //记录购票开始时间
long beginTime = System.currentTimeMillis();
for(Thread t : threadSet) {
//开始购票
t.start();
} //记录购票结束时间
long entTime;
while(true) {
//除去mian线程之外的所有线程结果后在记录结束时间
if(Thread.activeCount() == 1) {
entTime = System.currentTimeMillis();
break;
}
}
//开始统计
System.out.println("票号生成类实例对象数目:"+new HashSet(hanlderList).size());
System.out.println("共出票:"+ticketNumberList.size()+"张");
System.out.println("实际出票:"+new HashSet(ticketNumberList).size()+"张");
System.out.println("出票用时:"+(entTime - beginTime)+" 毫秒");
}
}

  结合着代码中的注释,相信这部分测试代码理解起来并不难,首先初始化10000个线程,相当于10000个用户同时购票,然后启动这10000个线程开始购票,结束后做统计。

  这里对代码中的hanlderList和ticketNumberList进行一下说明:

  1,这连个List的作用是什么?这两个List是用来做统计的。

    hanlderList用来存放单例对象,然后在最后统计的部分会转换为Set,去除重复的对象,剩余的对象数量就是真正的单例对象数量。如果真的是但是模式的话,在最后的统计打印的时候,票号生成类实例对象数目,应该是1。

    ticketNumberList是用来存放票号的,同样的在最后的统计部分也会转换为Set去重,如果真的有存在重复的票号,那么打印信息中的实际出票数量应该小于共出票数量

  2,这两个List为什么使用Vector而不是ArrayList,因为ArrayList是线程不安全的,如果使用ArrayList,在最后的统计中ArrayList 会出现null,这样我们的数据就不准确了。

  那么,开始测试。

五,第三中单例模式的测试结果

  右键 -> Run As -> Java Application。打印结果:

当前购票人数:10000 人
票号生成类实例对象数目:19
共出票:10000张
实际出票:9751张
出票用时:1130 毫秒

  可以看到:

  票号生成类实例对象数目:19

  说明不只是有一个单例对象产生,原因在上述的文章中也做了解释说明。同时“共出票“实际出票数量”小于“共出票”属性,说明产生了票号相同的票。

  ok,线程不安全的第三种单例示例结果之后,还有7中可用的线程安全的实现方式,我们就从1-8的顺序逐一检测,并通过执行时间来检测效率高低。

六,测试第一种单例模式:使用静态属性,并初始化单例

  1,单例代码

package com.zcz.singleton;

public class TicketNumberHandler1 extends TicketNumberHandler{
// 饿汉式,在类加载的时候初始化对象
private static TicketNumberHandler1 INSTANCE = new TicketNumberHandler1();
//私有化构造方法
private TicketNumberHandler1() {};
/**
* 获取单例实例
* @return
*/
public static TicketNumberHandler1 getInstance() {
return INSTANCE;
}
}

  2,修改测试类中使用的单例  

Thread t = new Thread() {
public void run() {
// TicketNumberHandler handler = TicketNumberHandler3.getInsatance();
TicketNumberHandler handler = TicketNumberHandler1.getInstance();
Long ticketNumber = handler.getTicketNumber();
ticketNumberList.add(ticketNumber);
};
};

  3,测试结果

当前购票人数:10000 人
票号生成类实例对象数目:1
共出票:10000张
实际出票:10000张
出票用时:1093 毫秒

  跟上一次的打印结果相比对,票号生成类实例对象数目确实只有一个了,这说明第一种单例模式,在多线程下是可以正确使用的。

  而且,实际出票数量和共出票数量相同,也是没有出现重复的票号的。但是真的是这样的吗?我么把用户数量调整到20000人,多执行几次代码试试看,你会发现偶尔会出现下面的打印结果:

当前购票人数:20000 人
票号生成类实例对象数目:1
共出票:20000张
实际出票:19996张
出票用时:5291 毫秒

  票号生成类的实例对象一直是1,这没问题,因为单例模式在多线程环境下正确执行了。

  但是实际出票数量小于了共出票数量,这说明出现了重复的票号,为什么呢?因为我们票号的生成方法,不是线程安全的

public Long getTicketNumber() {
return nextUniqueNumber++;
}

  代码中的nextUniqueNumber++是不具备原子性的,虽然看起来只有一行代码,但是实际上执行了三个步骤:读取nextUniqueNumber的值,将nextUniqueNumber的值加一,将结果赋值给nextUniqueNumber。

  所以出现重复票号的原因在于:在赋值没有结束前,有多个线程读取了值。

  怎么优化呢?最简单的就是使用同步锁。在getTicketNumber上添加关键字synchronized。

public synchronized Long getTicketNumber() {
return nextUniqueNumber++;
}

  还有另外一个方法,就是使用线程安全的AtomicLong

package com.zcz.singleton;

import java.util.concurrent.atomic.AtomicLong;

public class TicketNumberHandler {
private AtomicLong nextUniqueNumber = new AtomicLong();
//记录下一个唯一的号码
// private long nextUniqueNumber = 1;
/**
* 返回生成的号码
* @return
*/
public synchronized Long getTicketNumber() {
// return nextUniqueNumber++;
return nextUniqueNumber.incrementAndGet();
}
}

  ok,解决了这里的问题之后,我们将用户人数,重新调整到10000人,运行10次,统计平均执行时间:1154.3毫秒

七,测试第二种单例模式:使用静态代码块

  1,单例代码

package com.zcz.singleton;

public class TicketNumberHandler2 extends TicketNumberHandler {
// 饿汉式
private static TicketNumberHandler2 INSTANCE; //使用静态代码块,初始化对象
static {
INSTANCE = new TicketNumberHandler2();
}
//私有化构造方法
private TicketNumberHandler2() {};
/**
* 获取单例实例
* @return
*/
public static TicketNumberHandler2 getInstance() {
return INSTANCE;
}
}

  2,修改测试代码

Thread t = new Thread() {
public void run() {
// TicketNumberHandler handler = TicketNumberHandler3.getInsatance();
// TicketNumberHandler handler = TicketNumberHandler1.getInstance();
TicketNumberHandler handler = TicketNumberHandler2.getInstance();
hanlderList.add(handler); Long ticketNumber = handler.getTicketNumber();
ticketNumberList.add(ticketNumber);
};
};

  3,测试结果

当前购票人数:10000 人
票号生成类实例对象数目:1
共出票:10000张
实际出票:10000张
出票用时:1234 毫秒

  单例模式成功,出票数量正确,运行10次平均执行时间:1237.1毫秒

八,测试第四种单例模式:使用方法同步锁(synchronized)

  1,单例代码

package com.zcz.singleton;

public class TicketNumberHandler4 extends TicketNumberHandler {
//保存单例实例对象
private static TicketNumberHandler4 INSTANCE;
//私有化构造方法
private TicketNumberHandler4() {}; /**
* 懒汉式,在第一次获取单例对象的时候初始化对象
* @return
*/
public synchronized static TicketNumberHandler4 getInsatance() {
if(INSTANCE == null) {
try {
//这里为什么要让当前线程睡眠1毫秒呢?
//因为在正常的业务逻辑中,单利模式的类不可能这么简单,所以实例化时间会多一些
//让当前线程睡眠1毫秒
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
INSTANCE = new TicketNumberHandler4();
}
return INSTANCE;
}
}

  2,修改测试代码

Thread t = new Thread() {
public void run() {
// TicketNumberHandler handler = TicketNumberHandler3.getInsatance();
// TicketNumberHandler handler = TicketNumberHandler1.getInstance();
// TicketNumberHandler handler = TicketNumberHandler2.getInstance();
TicketNumberHandler handler = TicketNumberHandler4.getInsatance();
hanlderList.add(handler);
Long ticketNumber = handler.getTicketNumber();
ticketNumberList.add(ticketNumber);
};
};

  3,测试结果

当前购票人数:10000 人
票号生成类实例对象数目:1
共出票:10000张
实际出票:10000张
出票用时:1079 毫秒

    单例模式成功,出票数量正确,运行10次平均执行时间:1091.86毫秒

九,测试第五种单例模式:使用同步代码块

  1,单例代码

package com.zcz.singleton;

public class TicketNumberHandler5 extends TicketNumberHandler {
//保存单例实例对象
private static TicketNumberHandler5 INSTANCE;
//私有化构造方法
private TicketNumberHandler5() {}; /**
* 懒汉式,在第一次获取单例对象的时候初始化对象
* @return
*/
public static TicketNumberHandler5 getInsatance() {
if(INSTANCE == null) {
synchronized (TicketNumberHandler5.class) {
try {
//这里为什么要让当前线程睡眠1毫秒呢?
//因为在正常的业务逻辑中,单利模式的类不可能这么简单,所以实例化时间会多一些
//让当前线程睡眠1毫秒
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
INSTANCE = new TicketNumberHandler5();
}
}
return INSTANCE;
}
}

  2,修改测试代码

Thread t = new Thread() {
public void run() {
// TicketNumberHandler handler = TicketNumberHandler3.getInsatance();
// TicketNumberHandler handler = TicketNumberHandler1.getInstance();
// TicketNumberHandler handler = TicketNumberHandler2.getInstance();
// TicketNumberHandler handler = TicketNumberHandler4.getInsatance();
TicketNumberHandler handler = TicketNumberHandler5.getInsatance();
hanlderList.add(handler);
Long ticketNumber = handler.getTicketNumber();
ticketNumberList.add(ticketNumber);
};
};

  3,测试结果

当前购票人数:10000 人
票号生成类实例对象数目:1
共出票:10000张
实际出票:10000张
出票用时:1117 毫秒

    单例模式成功,出票数量正确,运行10次平均执行时间:1204.1毫秒

十,测试第六种单例模式:双重检查

  1,单例代码

package com.zcz.singleton;

public class TicketNumberHandler6 extends TicketNumberHandler {
//保存单例实例对象
private static TicketNumberHandler6 INSTANCE;
//私有化构造方法
private TicketNumberHandler6() {}; /**
* 懒汉式,在第一次获取单例对象的时候初始化对象
* @return
*/
public static TicketNumberHandler6 getInsatance() {
//双重检查
if(INSTANCE == null) {
synchronized (TicketNumberHandler5.class) {
try {
//这里为什么要让当前线程睡眠1毫秒呢?
//因为在正常的业务逻辑中,单利模式的类不可能这么简单,所以实例化时间会多一些
//让当前线程睡眠1毫秒
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(INSTANCE == null) {
INSTANCE = new TicketNumberHandler6();
}
}
}
return INSTANCE;
}
}

  2,修改测试代码

Thread t = new Thread() {
public void run() {
// TicketNumberHandler handler = TicketNumberHandler3.getInsatance();
// TicketNumberHandler handler = TicketNumberHandler1.getInstance();
// TicketNumberHandler handler = TicketNumberHandler2.getInstance();
// TicketNumberHandler handler = TicketNumberHandler4.getInsatance();
// TicketNumberHandler handler = TicketNumberHandler5.getInsatance();
TicketNumberHandler handler = TicketNumberHandler6.getInsatance();
hanlderList.add(handler);
Long ticketNumber = handler.getTicketNumber();
ticketNumberList.add(ticketNumber);
};
};

  3,测试结果

当前购票人数:10000 人
票号生成类实例对象数目:1
共出票:10000张
实际出票:10000张
出票用时:1041 毫秒

    单例模式成功,出票数量正确,运行10次平均执行时间:1117.1毫秒

十一,测试第七种单例模式:使用静态内部类

  1,单例代码

package com.zcz.singleton;

public class TicketNumberHandler7 extends TicketNumberHandler {
//私有化构造器
public TicketNumberHandler7() {}; //静态内部类
private static class TicketNumberHandler7Instance{
private static final TicketNumberHandler7 INSTANCE = new TicketNumberHandler7();
} public static TicketNumberHandler7 getInstance() {
return TicketNumberHandler7Instance.INSTANCE;
}
}

  2,修改测试代码

Thread t = new Thread() {
public void run() {
// TicketNumberHandler handler = TicketNumberHandler3.getInsatance();
// TicketNumberHandler handler = TicketNumberHandler1.getInstance();
// TicketNumberHandler handler = TicketNumberHandler2.getInstance();
// TicketNumberHandler handler = TicketNumberHandler4.getInsatance();
// TicketNumberHandler handler = TicketNumberHandler5.getInsatance();
// TicketNumberHandler handler = TicketNumberHandler6.getInsatance();
TicketNumberHandler handler = TicketNumberHandler7.getInstance();
hanlderList.add(handler);
Long ticketNumber = handler.getTicketNumber();
ticketNumberList.add(ticketNumber);
};
};

  3,测试结果

当前购票人数:10000 人
票号生成类实例对象数目:1
共出票:10000张
实际出票:10000张
出票用时:1250 毫秒

    单例模式成功,出票数量正确,运行10次平均执行时间:1184.4毫秒

十二,测试第八种单例模式:使用枚举

  1,单例代码

package com.zcz.singleton;

import java.util.concurrent.atomic.AtomicLong;

public enum TicketNumberHandler8 {
INSTANCE;
private AtomicLong nextUniqueNumber = new AtomicLong();
//记录下一个唯一的号码
// private long nextUniqueNumber = 1;
/**
* 返回生成的号码
* @return
*/
public synchronized Long getTicketNumber() {
// return nextUniqueNumber++;
return nextUniqueNumber.incrementAndGet();
}
}

  2,修改测试代码

public static void main(String[] args) {
// 用户人数
int userNumber = 10000;
// 保存用户线程
Set<Thread> threadSet = new HashSet(); // 用于存放TicketNumberHandler实例对象
List<TicketNumberHandler8> hanlderList = new Vector();
// 保存生成的票号
List<Long> ticketNumberList = new Vector(); // 定义购票线程,一个线程模拟一个用户
for(int i=0;i<userNumber;i++) {
Thread t = new Thread() {
public void run() {
// TicketNumberHandler handler = TicketNumberHandler3.getInsatance();
// TicketNumberHandler handler = TicketNumberHandler1.getInstance();
// TicketNumberHandler handler = TicketNumberHandler2.getInstance();
// TicketNumberHandler handler = TicketNumberHandler4.getInsatance();
// TicketNumberHandler handler = TicketNumberHandler5.getInsatance();
// TicketNumberHandler handler = TicketNumberHandler6.getInsatance();
// TicketNumberHandler handler = TicketNumberHandler7.getInstance();
TicketNumberHandler8 handler = TicketNumberHandler8.INSTANCE;
hanlderList.add(handler);
Long ticketNumber = handler.getTicketNumber();
ticketNumberList.add(ticketNumber);
};
};
threadSet.add(t);
}
System.out.println("当前购票人数:"+threadSet.size()+" 人"); //记录购票开始时间
long beginTime = System.currentTimeMillis();
for(Thread t : threadSet) {
//开始购票
t.start();
} //记录购票结束时间
long entTime;
while(true) {
//除去mian线程之外的所有线程结果后再记录时间
if(Thread.activeCount() == 1) {
entTime = System.currentTimeMillis();
break;
}
}
//开始统计
System.out.println("票号生成类实例对象数目:"+new HashSet(hanlderList).size());
System.out.println("共出票:"+ticketNumberList.size()+"张");
System.out.println("实际出票:"+new HashSet(ticketNumberList).size()+"张");
System.out.println("出票用时:"+(entTime - beginTime)+" 毫秒");
}

  3,测试结果

当前购票人数:10000 人
票号生成类实例对象数目:1
共出票:10000张
实际出票:10000张
出票用时:1031 毫秒

    单例模式成功,出票数量正确,运行10次平均执行时间:1108毫秒

十三,总结  

  线程安全就不再多说,除去第三种方式。其他的都可以。

  效率总结表:

单例模式名称 平均十次执行时间(毫秒)
第一种(使用静态属性,并初始化单例) 1154.3
第二种(使用静态代码块) 1237.1
第四种(使用方法同步锁) 1091.86
第五种(使用同步代码块) 1204.1
第六种(双重检查) 1117.1
第七种(使用静态内部类) 1184.4
第八种(使用枚举) 1108

  跟我预想的不同,没有想到的是,竟然是第四种方法的效率最高,很可能跟我测试数据的数量有关系(10000个用户)。效率的话就不多做评论了,大家有兴趣的话可以自己亲自试一下。别忘记告诉我测试的结果哦。

  从代码行数来看,使用枚举是最代码最少的方法了。

  ok,这篇文章到这里就结束了,虽然在效率上没有结论,但是,在线程安全方面是明确了的。

相关java设计模式的文章:

  JAVA设计模式-动态代理(Proxy)示例及说明

  JAVA设计模式-动态代理(Proxy)源码分析

  JAVA设计模式-单例模式(Singleton)线程安全与效率


原创不易,转载请注明出处:https://www.cnblogs.com/zhangchengzi/p/9718507.html

JAVA设计模式-单例模式(Singleton)线程安全与效率的更多相关文章

  1. Java设计模式-单例模式及线程安全问题

    单例模式是非常常用的设计模式,他确保了一个类只有一个对象,并且这个对象是自己创建的,外界可以获取使用到这个对象. 单例模式一般有两种:懒汉式,饿汉式(其实还有一种登记式,把创建的对象放在map集合中, ...

  2. 设计模式 单例模式(Singleton) [ 转载 ]

    设计模式 单例模式(Singleton) [ 转载 ] 转载请注明出处:http://cantellow.iteye.com/blog/838473 前言 懒汉:调用时才创建对象 饿汉:类初始化时就创 ...

  3. java设计模式单例模式 ----懒汉式与饿汉式的区别

    常用的五种单例模式实现方式 ——主要: 1.饿汉式(线程安全,调用率高,但是,不能延迟加载.) 2.懒汉式(线程安全,调用效率不高,可以延时加载.) ——其他: 1.双重检测锁式(由于JVM底层内部模 ...

  4. Java设计模式の单例模式

    -------------------------------------------------- 目录 1.定义 2.常见的集中单例实现 a.饿汉式,线程安全 但效率比较低 b.单例模式的实现:饱 ...

  5. 【设计模式】Java设计模式 - 单例模式

    [设计模式]Java设计模式 - 单例模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 分享学习心得,欢迎指正,大家一起学习成长! 原创作品,更多关注我CSDN: ...

  6. 设计模式 单例模式(Singleton) [ 转载2 ]

    设计模式 单例模式(Singleton) [ 转载2 ] @author java_my_life 单例模式的结构 单例模式的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类 ...

  7. Java设计模式 - - 单例模式 装饰者模式

    Java设计模式 单例模式 装饰者模式 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 静态代理模式:https://www.cnblogs.com/StanleyBlogs/p/1 ...

  8. Java 设计模式 —— 单例模式

    1. 概念: 单例模式是一种常用的软件设计模式.核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源.如果 ...

  9. JAVA设计模式--单例模式

    单例设计模式 Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点. 核心知识点如下: (1) 将采用单例 ...

随机推荐

  1. 打印机服务配置篇WindowsServer2008

    本次配置Server2008 打印服务器    目的实现Kingdee远程打印服务,直接在金蝶客户端部署打印机服务器 服务器角色: --打印服务器 --LPD服务 --Internet打印 *打印服务 ...

  2. SpringBoot进阶教程(六十一)intellij idea project下建多个module搭建架构(下)

    在上一篇文章<SpringBoot进阶教程(六十)intellij idea project下建多个module(上)>中,我们已经介绍了在intellij idea中创建project之 ...

  3. arcgis三维球中加载2000坐标系出现错误(The tiling scheme of this layer is not supported by SceneView)

    目前我们国家测绘地理信息的坐标体系基准是国家2000坐标系CGCS2000.各类地图组件如OpenLayers.Mapbox.Cesuim和ArcGIS Javascrip等都主要是支持WGS84(w ...

  4. python 23 继承

    目录 继承--inheritance 1. 面向对象继承: 2. 单继承 2.1 类名执行父类的属性.方法 2.2 子类对象执行父类的属性.方法 2.3 执行顺序 2.4 既要执行子类的方法,又要执行 ...

  5. Java 并发编程(一):摩拳擦掌

    这篇文章的标题原本叫做——Java 并发编程(一):简介,作者名叫小二.但我在接到投稿时觉得这标题不够新颖,不够吸引读者的眼球,就在发文的时候强行修改了标题(也不咋滴). 小二是一名 Java 程序员 ...

  6. Vue+webpack项目的多环境打包配置

    背景:由于需要将应用部署到线上开发环境.线上测试环境.线上预发环境.线上生产环境,而每个环境的访问地址是不同的.如果每次更改请求地址未免有些繁琐,就考虑在本地进行一次性配置. 代码管理工具:git 代 ...

  7. Python Web 之 Flask

    FLASK 一.概述 flask是一个基于python并依赖于Jinja2模板引擎和WerkZeug WSGI(Web Server Gatewey InterFace.web)服务的框架 WSGI: ...

  8. TypeScript进阶开发——ThreeJs基础实例,从入坑到入门

    前言 我们前面使用的是自己编写的ts,以及自己手动引入的jquery,由于第三方库采用的是直接引入js,没有d.ts声明文件,开发起来很累,所以一般情况下我们使用npm引入第三方的库,本文记录使用np ...

  9. CodeForces - 938D-Buy a Ticket+最短路

    Buy a Ticket 题意:有n个点和m条路(都收费),n个点在开演唱会,门票不同,对于生活在n个点的小伙伴,要求计算出每个小伙伴为了看一场演唱会要花费的最小价格: 思路: 这道题我一开始觉得要对 ...

  10. poj 3026 Borg Maze(最小生成树+bfs)

    题目链接:http://poj.org/problem?id=3026 题意:题意就是从起点开始可以分成多组总权值就是各组经过的路程,每次到达一个‘A'点可以继续分组,但是在路上不能分组 于是就是明显 ...