1.SimpleDateFormat非线程安全的问题

  类SimpleDateFormat主要负责日期的转换与格式化,但在多线程环境中,使用此类容易造成数据转换及处理的不正确,因为SimpleDateFormat类并不是线程安全的。

1.多线程中存在的问题:

package cn.qlq.thread.seventeen;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class Demo1 extends Thread {
private static final Logger LOGGER = LoggerFactory.getLogger(Demo1.class);
private SimpleDateFormat simpleDateFormat;
private String dateStr; public Demo1(SimpleDateFormat simpleDateFormat, String dateStr) {
super();
this.simpleDateFormat = simpleDateFormat;
this.dateStr = dateStr;
} @Override
public void run() {
try {
Date parse = simpleDateFormat.parse(dateStr);
String format = simpleDateFormat.format(parse).toString();
LOGGER.info("threadName ->{} ,dateStr ->{},格式化后的->{}", Thread.currentThread().getName(), dateStr, format);
} catch (ParseException e) {
LOGGER.error("parseException", e);
}
} public static void main(String[] args) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String[] dateStrs = new String[] { "2018-01-01", "2018-01-03", "2018-01-03" };
Thread[] threads = new Thread[3];
for (int i = 0; i < 3; i++) {
threads[i] = new Demo1(simpleDateFormat, dateStrs[i]);
}
for (int i = 0; i < 3; i++) {
threads[i].start();
} }
}

结果:

10:07:27 [cn.qlq.thread.seventeen.Demo1]-[INFO] threadName ->Thread-0 ,dateStr ->2018-01-01,格式化后的->2001-01-01
10:07:27 [cn.qlq.thread.seventeen.Demo1]-[INFO] threadName ->Thread-1 ,dateStr ->2018-01-03,格式化后的->2001-01-03
10:07:27 [cn.qlq.thread.seventeen.Demo1]-[INFO] threadName ->Thread-2 ,dateStr ->2018-01-03,格式化后的->2001-01-01

多次执行发现结果不固定,有时候报错如下:

Exception in thread "Thread-1" Exception in thread "Thread-0" java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1110)
at java.lang.Double.parseDouble(Double.java:540)
at java.text.DigitList.getDouble(DigitList.java:168)
at java.text.DecimalFormat.parse(DecimalFormat.java:1321)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1793)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455)
at java.text.DateFormat.parse(DateFormat.java:355)
at cn.qlq.thread.seventeen.Demo1.run(Demo1.java:24)
java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1110)
at java.lang.Double.parseDouble(Double.java:540)
at java.text.DigitList.getDouble(DigitList.java:168)
at java.text.DecimalFormat.parse(DecimalFormat.java:1321)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1793)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455)
at java.text.DateFormat.parse(DateFormat.java:355)
at cn.qlq.thread.seventeen.Demo1.run(Demo1.java:24)
10:08:20 [cn.qlq.thread.seventeen.Demo1]-[INFO] threadName ->Thread-2 ,dateStr ->2018-01-03,格式化后的->2200-01-03

  因为上面多个线程使用了同一个SimpleDateFormat实例,所以在多线程环境下使用此类是不安全的。

补充:SimpleDateFormat线程非安全的原因:

  SimpleDateFormat内部继承了一个DateFormat的calendar成员变量,所以多个线程共用同一个calendar,所以容易造成线程非安全。

public class SimpleDateFormat extends DateFormat {
// Called from Format after creating a FieldDelegate
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);
...
}
}
public abstract class DateFormat extends Format {
protected Calendar calendar;
}

补充:再次查看apache的commons-lang包的工具类:DateFormatUtils

   其方法内部新建了一个局部变量,并进行格式化,因此不存在线程非安全的问题

    public static String format(final Date date, final String pattern, final TimeZone timeZone, final Locale locale) {
final FastDateFormat df = FastDateFormat.getInstance(pattern, timeZone, locale);
return df.format(date);
}
    public String format(final Date date) {
final Calendar c = newCalendar(); // hard code GregorianCalendar
c.setTime(date);
return applyRulesToString(c);
}

2.解决办法

1.每个线程使用一个SimpleDateFormat

修改main方法中的代码如下:(每个线程使用一个SimpleDateFormat)

        String[] dateStrs = new String[] { "2018-01-01", "2018-01-03", "2018-01-03" };
Thread[] threads = new Thread[3];
for (int i = 0; i < 3; i++) {
threads[i] = new Demo2(new SimpleDateFormat("yyyy-MM-dd"), dateStrs[i]);
}
for (int i = 0; i < 3; i++) {
threads[i].start();
}

2.使用ThreadLocal解决上面问题

  在前面学习过ThreadLocal的作用,可以实现多线程之间的隔离。

package cn.qlq.thread.seventeen;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class Demo2 extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Demo2.class);
private static final ThreadLocal<SimpleDateFormat> THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>(); public static SimpleDateFormat getSimpleDateFormat() {
SimpleDateFormat simpleDateFormat2 = THREAD_LOCAL.get();
if (simpleDateFormat2 == null) {
simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd");
THREAD_LOCAL.set(simpleDateFormat2);
}
return simpleDateFormat2;
} private SimpleDateFormat simpleDateFormat;
private String dateStr; public Demo2(String dateStr) {
super();
this.dateStr = dateStr;
} @Override
public void run() {
try {
// 从ThreadLocal中获取simpleDateFormat
this.simpleDateFormat = Demo2.getSimpleDateFormat(); Date parse = simpleDateFormat.parse(dateStr);
String format = simpleDateFormat.format(parse).toString();
LOGGER.info("threadName ->{} ,dateStr ->{},格式化后的->{}", Thread.currentThread().getName(), dateStr, format);
} catch (ParseException e) {
LOGGER.error("parseException", e);
}
} public static void main(String[] args) {
String[] dateStrs = new String[] { "2018-01-01", "2018-01-03", "2018-01-03" };
Thread[] threads = new Thread[3];
for (int i = 0; i < 3; i++) {
threads[i] = new Demo2(dateStrs[i]);
}
for (int i = 0; i < 3; i++) {
threads[i].start();
} }
}

结果:

10:37:53 [cn.qlq.thread.seventeen.Demo2]-[INFO] threadName ->Thread-1 ,dateStr ->2018-01-03,格式化后的->2018-01-03
10:37:53 [cn.qlq.thread.seventeen.Demo2]-[INFO] threadName ->Thread-0 ,dateStr ->2018-01-01,格式化后的->2018-01-01
10:37:53 [cn.qlq.thread.seventeen.Demo2]-[INFO] threadName ->Thread-2 ,dateStr ->2018-01-03,格式化后的->2018-01-03

2.线程中出现异常的处理

  线程中也容易出现异常。在多线程中也可以对多线程中的异常进行捕捉,使用的是UncaughtExceptionHandler类,从而可以对发生的异常进行有效的处理。

package cn.qlq.thread.seventeen;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class Demo3 extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Demo3.class); @Override
public void run() {
int i = 1 / 0;
} public static void main(String[] args) {
new Demo3().start();
}
}

结果:

Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at cn.qlq.thread.seventeen.Demo3.run(Demo3.java:12)

  使用UncaughtExceptionHandler捕捉多线程中的异常。查看线程类Thread的源码,发现其有两个异常处理器,一个是所有线程共享的默认静态异常处理器、另一个是线程独有的成员属性。

    // null unless explicitly set
private volatile UncaughtExceptionHandler uncaughtExceptionHandler; // null unless explicitly set
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;

   public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(
new RuntimePermission("setDefaultUncaughtExceptionHandler")
);
} defaultUncaughtExceptionHandler = eh;
}
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
checkAccess();
uncaughtExceptionHandler = eh;
}

(1)多所有线程设置默认的默认异常处理器

package cn.qlq.thread.seventeen;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class Demo5 extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Demo5.class); @Override
public void run() {
int i = 1 / 0;
} public static void main(String[] args) {
Demo5.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("所有Demo5线程默认的异常处理器, threadName -> {}", t.getName(), e);
}
});
Demo5 demo5 = new Demo5();
demo5.start();
}
}

结果:

14:12:54 [cn.qlq.thread.seventeen.Demo5]-[ERROR] 所有Demo5线程默认的异常处理器, threadName -> Thread-0
java.lang.ArithmeticException: / by zero
at cn.qlq.thread.seventeen.Demo5.run(Demo5.java:12)

(2)对单个线程设置异常处理器

package cn.qlq.thread.seventeen;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class Demo5 extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Demo5.class); @Override
public void run() {
int i = 1 / 0;
} public static void main(String[] args) {
Demo5 demo5 = new Demo5();
// 设置异常捕捉器
demo5.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("单独给线程设置的, threadName -> {}", t.getName(), e);
}
});
demo5.start();
}
}

结果:

14:15:00 [cn.qlq.thread.seventeen.Demo5]-[ERROR] 单独给线程设置的, threadName -> Thread-0
java.lang.ArithmeticException: / by zero
at cn.qlq.thread.seventeen.Demo5.run(Demo5.java:12)

(3)如果都设置查看默认的生效的异常处理器---是单独设置的异常处理器uncaughtExceptionHandler发挥作用

package cn.qlq.thread.seventeen;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class Demo5 extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Demo5.class); @Override
public void run() {
int i = 1 / 0;
} public static void main(String[] args) {
// 设置全局的
Demo5.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("所有Demo5线程默认的异常处理器, threadName -> {}", t.getName(), e);
}
}); Demo5 demo5 = new Demo5();
// 设置单独的
demo5.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("单独给线程设置的, threadName -> {}", t.getName(), e);
}
});
demo5.start();
}
}

结果:(生效的是线程单独设置的异常处理器)

14:19:25 [cn.qlq.thread.seventeen.Demo5]-[ERROR] 单独给线程设置的, threadName -> Thread-0
  java.lang.ArithmeticException: / by zero
  at cn.qlq.thread.seventeen.Demo5.run(Demo5.java:12)

3.线程组出现异常的处理

3.1线程组内出现异常

package cn.qlq.thread.seventeen;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class Demo3 extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Demo3.class);
private Integer num; public Demo3(Integer num, ThreadGroup threadGroup) {
super(threadGroup, num + "" + Math.random());
this.num = num;
} @Override
public void run() {
int i = 1 / num;
while (true) {
try {
Thread.sleep(1 * 1000);
LOGGER.info("num = {}", num++);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
ThreadGroup threadGroup = new ThreadGroup("myGroup");
Thread t1 = new Demo3(0, threadGroup);
Thread t2 = new Demo3(0, threadGroup);
Thread t3 = new Demo3(1, threadGroup);
t1.start();
t2.start();
t3.start();
}
}

结果:(t1,t2由于异常停止,t3仍然在执行while循环)。

查看线程信息

Administrator@MicroWin10-1535 MINGW64 /e/xiangmu/ThreadStudy (master)
$ jstack 2712
2019-01-03 12:00:26
Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.80-b11 mixed mode): "DestroyJavaVM" prio=6 tid=0x000000000c8f5000 nid=0xbc8 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE "10.0050164194939836815" prio=6 tid=0x000000000c8f6800 nid=0x54d4 waiting on condition [0x000000000ce4f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at cn.qlq.thread.seventeen.Demo3.run(Demo3.java:21) "Service Thread" daemon prio=6 tid=0x000000000ae14000 nid=0x31d0 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" daemon prio=10 tid=0x000000000adeb800 nid=0x754 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" daemon prio=10 tid=0x000000000adea000 nid=0x22e4 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE "Attach Listener" daemon prio=10 tid=0x000000000ade8800 nid=0x51c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE "Signal Dispatcher" daemon prio=10 tid=0x000000000ae01000 nid=0x2914 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE "Finalizer" daemon prio=8 tid=0x000000000adae800 nid=0x1ef8 in Object.wait() [0x000000000c14f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000007d6204858> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x00000007d6204858> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209) "Reference Handler" daemon prio=10 tid=0x000000000ada5800 nid=0x3820 in Object.wait() [0x000000000c04f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000007d6204470> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
- locked <0x00000007d6204470> (a java.lang.ref.Reference$Lock) "VM Thread" prio=10 tid=0x000000000ada1800 nid=0x2f54 runnable "GC task thread#0 (ParallelGC)" prio=6 tid=0x0000000002a76800 nid=0x4d04 runnable "GC task thread#1 (ParallelGC)" prio=6 tid=0x0000000002a78000 nid=0x1bbc runnable "GC task thread#2 (ParallelGC)" prio=6 tid=0x0000000002a7b000 nid=0x11a0 runnable "GC task thread#3 (ParallelGC)" prio=6 tid=0x0000000002a7c800 nid=0x4b80 runnable "VM Periodic Task Thread" prio=10 tid=0x000000000ae1e800 nid=0x5a4 waiting on condition JNI global references: 184

3.2 线程组内处理异常

  从上面的运行结果也可以看出线程组中的一个线程出现异常不会影响其它线程的运行。

1.线程组统一对异常进行处理,记录异常

  继承ThreadGroup并且重写uncaughtException方法。

package cn.qlq.thread.seventeen;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class Demo3 extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Demo3.class);
private Integer num; public Demo3(Integer num, ThreadGroup threadGroup) {
super(threadGroup, num + "" + Math.random());
this.num = num;
} @Override
public void run() {
int i = 1 / num;
while (true) {
try {
Thread.sleep(1 * 1000);
LOGGER.info("num = {}", num++);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
MyThreadGroup threadGroup = new MyThreadGroup(new ThreadGroup("myGroup"));
Thread t1 = new Demo3(0, threadGroup);
Thread t2 = new Demo3(0, threadGroup);
Thread t3 = new Demo3(1, threadGroup);
t1.start();
t2.start();
t3.start();
}
} class MyThreadGroup extends ThreadGroup {
private static final Logger LOGGER = LoggerFactory.getLogger(MyThreadGroup.class); public MyThreadGroup(ThreadGroup threadGroup) {
super(threadGroup, threadGroup.getName());
} @Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("UncaughtException , threadName -> {},ThreadGroupName -> {}", t.getName(),
t.getThreadGroup().getName(), e);
}
}

结果:

2.线程组处理异常,一个线程异常停止组内所有异常

package cn.qlq.thread.seventeen;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class Demo3 extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Demo3.class);
private Integer num; public Demo3(Integer num, ThreadGroup threadGroup) {
super(threadGroup, num + "" + Math.random());
this.num = num;
} @Override
public void run() {
int i = 1 / num;
while (!this.isInterrupted()) {
LOGGER.info("num = {}", num++);
}
} public static void main(String[] args) {
MyThreadGroup threadGroup = new MyThreadGroup(new ThreadGroup("myGroup"));
Thread t1 = new Demo3(0, threadGroup);
Thread t2 = new Demo3(0, threadGroup);
Thread t3 = new Demo3(1, threadGroup);
t1.start();
t2.start();
t3.start();
}
} class MyThreadGroup extends ThreadGroup {
private static final Logger LOGGER = LoggerFactory.getLogger(MyThreadGroup.class); public MyThreadGroup(ThreadGroup threadGroup) {
super(threadGroup, threadGroup.getName());
} @Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("UncaughtException , threadName -> {},ThreadGroupName -> {},向组发出中断信号", t.getName(),
t.getThreadGroup().getName(), e);
this.interrupt();
}
}

结果:

12:14:56 [cn.qlq.thread.seventeen.Demo3]-[INFO] num = 1
12:14:56 [cn.qlq.thread.seventeen.MyThreadGroup]-[ERROR] UncaughtException , threadName -> 00.03911857279295183,ThreadGroupName -> myGroup,向组发出中断信号
java.lang.ArithmeticException: / by zero
at cn.qlq.thread.seventeen.Demo3.run(Demo3.java:18)
12:14:56 [cn.qlq.thread.seventeen.MyThreadGroup]-[ERROR] UncaughtException , threadName -> 00.9116153677767221,ThreadGroupName -> myGroup,向组发出中断信号
java.lang.ArithmeticException: / by zero
at cn.qlq.thread.seventeen.Demo3.run(Demo3.java:18)
12:14:56 [cn.qlq.thread.seventeen.Demo3]-[INFO] num = 2

4.线程异常处理的传递

  前面介绍了多种线程的处理方式,如果将多个异常处理方式放在一起运行,如果线程设置了异常处理首先生效的是线程的异常处理器;如果线程没有设置,生效的是线程组的异常处理器。

package cn.qlq.thread.seventeen;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class Demo4 extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(Demo4.class);
private Integer num; public Demo4(Integer num, ThreadGroup threadGroup) {
super(threadGroup, num + "" + Math.random());
this.num = num;
} @Override
public void run() {
int i = 1 / num;
while (!this.isInterrupted()) {
LOGGER.info("num = {}", num++);
}
} public static void main(String[] args) {
MyThreadGroup2 threadGroup = new MyThreadGroup2(new ThreadGroup("myGroup"));
Thread t1 = new Demo4(0, threadGroup);
t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("线程中报错,threadName->{}", t.getName(), e);
}
}); t1.start();
Thread t2 = new Demo4(0, threadGroup);
t2.start();
}
} class MyThreadGroup2 extends ThreadGroup {
private static final Logger LOGGER = LoggerFactory.getLogger(MyThreadGroup2.class); public MyThreadGroup2(ThreadGroup threadGroup) {
super(threadGroup, threadGroup.getName());
} @Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("UncaughtException , threadName -> {},ThreadGroupName -> {}", t.getName(),
t.getThreadGroup().getName(), e);
}
}

结果:(t1被线程处理器捕捉,t2被线程组处理器捕捉)

13:51:07 [cn.qlq.thread.seventeen.Demo4]-[ERROR] 线程中报错,threadName->00.8052783699913576
java.lang.ArithmeticException: / by zero
at cn.qlq.thread.seventeen.Demo4.run(Demo4.java:18)
13:51:07 [cn.qlq.thread.seventeen.MyThreadGroup2]-[ERROR] UncaughtException , threadName -> 00.6447075404863479,ThreadGroupName -> myGroup
java.lang.ArithmeticException: / by zero
at cn.qlq.thread.seventeen.Demo4.run(Demo4.java:18)

如果在组中调用原来父类的方法会继续在console中打印错误日志:

class MyThreadGroup2 extends ThreadGroup {
private static final Logger LOGGER = LoggerFactory.getLogger(MyThreadGroup2.class); public MyThreadGroup2(ThreadGroup threadGroup) {
super(threadGroup, threadGroup.getName());
} @Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("UncaughtException , threadName -> {},ThreadGroupName -> {}", t.getName(),
t.getThreadGroup().getName(), e);
super.uncaughtException(t, e);
}
}

结果:

【多线程补充】SimpleDateFormat非线程安全与线程中、线程组中异常的处理的更多相关文章

  1. 零基础学习java------day18------properties集合,多线程(线程和进程,多线程的实现,线程中的方法,线程的声明周期,线程安全问题,wait/notify.notifyAll,死锁,线程池),

    1.Properties集合 1.1 概述: Properties类表示了一个持久的属性集.Properties可保存在流中或从流中加载.属性列表中每个键及其对应值都是一个字符串 一个属性列表可包含另 ...

  2. java多线程系类:JUC线程池:03之线程池原理(二)(转)

    概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...

  3. python_way ,day11 线程,怎么写一个多线程?,队列,生产者消费者模型,线程锁,缓存(memcache,redis)

    python11 1.多线程原理 2.怎么写一个多线程? 3.队列 4.生产者消费者模型 5.线程锁 6.缓存 memcache redis 多线程原理 def f1(arg) print(arg) ...

  4. posix 线程(一):线程模型、pthread 系列函数 和 简单多线程服务器端程序

    posix 线程(一):线程模型.pthread 系列函数 和 简单多线程服务器端程序 一.线程有3种模型,分别是N:1用户线程模型,1:1核心线程模型和N:M混合线程模型,posix thread属 ...

  5. QThread 与 QObject的关系(QObject可以用于多线程,可以发送信号调用存在于其他线程的slot函数,但GUI类不可重入)

    QThread 继承 QObject..它可以发送started和finished信号,也提供了一些slot函数. QObject.可以用于多线程,可以发送信号调用存在于其他线程的slot函数,也可以 ...

  6. 多线程面试题系列(7):经典线程同步 互斥量Mutex

    前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用互斥量Mutex来解决这个问题. 互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行为非常相似, ...

  7. 多线程(三) java中线程的简单使用

    java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依 ...

  8. 转发 Delphi中线程类TThread 实现多线程编程

    Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到,但基本上都是对TThread类的几个成员作一简单介绍,再说明一下Execute的实现和Synchr ...

  9. 非线程安全的HashMap 和 线程安全的ConcurrentHashMap

    在平时开发中,我们经常采用HashMap来作为本地缓存的一种实现方式,将一些如系统变量等数据量比较少的参数保存在HashMap中,并将其作为单例类的一个属性.在系统运行中,使用到这些缓存数据,都可以直 ...

随机推荐

  1. maomao的现在与未来

    今晚开始,maomao决定要成为一个和以前不一样的人了. 自从高一开始竞赛生涯以来,我的竞赛就一直处于不是很好,也不是很差的水平,就像我初中一年级到二年级一直处于的状态一样.一直以来,我从来没有对自己 ...

  2. 42套JavaScript深度解析教学视频!合集

    本文首发于:风云社区SCOEE(社区旨在普惠软件.图片.音乐.视频.素材.文档等互联网资源.为大众提供多样化的服务,以及主要涵盖学术科学.电脑技术.文化人文.体育健身等领域的知识和信息,获得用户的支持 ...

  3. [DUBBO] Unexpected error occur at send statistic, cause: Forbid consumer 192.168.3.151 access servic

    [DUBBO] Unexpected error occur at send statistic, cause: Forbid consumer 192.168.3.151 access servic ...

  4. Data Visualization – Banking Case Study Example (Part 1-6)

    python信用评分卡(附代码,博主录制) https://study.163.com/course/introduction.htm?courseId=1005214003&utm_camp ...

  5. hadoop 开发环境搭建

    一,安装java环境 添加java环境变量 vi /etc/profile   # add by tank export JAVA_HOME=/data/soft/jdk/jdk1.7.0_71 ex ...

  6. spring-mybatis-springMVC 整合

    这是一个spring mybatis springMVC 的整合 里面包括日志,druid,的配置.可以说是一个现成的模板,直接复制下来就能用. 首先是web.xml web.xml 中包括 Spri ...

  7. 【1】【leetcode-77】 组合

    (典型,做过似曾相识但不熟悉,基本知道怎么做但调试了一个多小时各种错) 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合. 示例: 输入: n = 4, k = 2 输出: ...

  8. HDU - 5071 Chat(模拟)

    原题链接 题意:有各种操作,模拟这个程序并输出每次操作的信息 分析:恶心模拟题...用个map记录一下各个等级女孩的谈话数,同时也便于查找权值为u的在不在队列里.因为n很小,其他就暴力模拟了. #in ...

  9. 牛客网数据库SQL实战(此处只有答案,没有表内容)

    1.查找最晚入职员工的所有信息   select * from employees order by hire_date desc limit 1; --limit n表示输出前n条数据,limit ...

  10. URLSearchParams 接口

    URLSearchParams 接口定义了很多个用来处理 URL 参数串的方法 他可以把对象转变成url上面查询信息的写法,例如a=1&b=2 可以把请求路由中的字符串   key=1 ,拼接 ...