【多线程补充】SimpleDateFormat非线程安全与线程中、线程组中异常的处理
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非线程安全与线程中、线程组中异常的处理的更多相关文章
- 零基础学习java------day18------properties集合,多线程(线程和进程,多线程的实现,线程中的方法,线程的声明周期,线程安全问题,wait/notify.notifyAll,死锁,线程池),
1.Properties集合 1.1 概述: Properties类表示了一个持久的属性集.Properties可保存在流中或从流中加载.属性列表中每个键及其对应值都是一个字符串 一个属性列表可包含另 ...
- java多线程系类:JUC线程池:03之线程池原理(二)(转)
概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...
- python_way ,day11 线程,怎么写一个多线程?,队列,生产者消费者模型,线程锁,缓存(memcache,redis)
python11 1.多线程原理 2.怎么写一个多线程? 3.队列 4.生产者消费者模型 5.线程锁 6.缓存 memcache redis 多线程原理 def f1(arg) print(arg) ...
- posix 线程(一):线程模型、pthread 系列函数 和 简单多线程服务器端程序
posix 线程(一):线程模型.pthread 系列函数 和 简单多线程服务器端程序 一.线程有3种模型,分别是N:1用户线程模型,1:1核心线程模型和N:M混合线程模型,posix thread属 ...
- QThread 与 QObject的关系(QObject可以用于多线程,可以发送信号调用存在于其他线程的slot函数,但GUI类不可重入)
QThread 继承 QObject..它可以发送started和finished信号,也提供了一些slot函数. QObject.可以用于多线程,可以发送信号调用存在于其他线程的slot函数,也可以 ...
- 多线程面试题系列(7):经典线程同步 互斥量Mutex
前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用互斥量Mutex来解决这个问题. 互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行为非常相似, ...
- 多线程(三) java中线程的简单使用
java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依 ...
- 转发 Delphi中线程类TThread 实现多线程编程
Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到,但基本上都是对TThread类的几个成员作一简单介绍,再说明一下Execute的实现和Synchr ...
- 非线程安全的HashMap 和 线程安全的ConcurrentHashMap
在平时开发中,我们经常采用HashMap来作为本地缓存的一种实现方式,将一些如系统变量等数据量比较少的参数保存在HashMap中,并将其作为单例类的一个属性.在系统运行中,使用到这些缓存数据,都可以直 ...
随机推荐
- MySQL存储过程实现动态执行SQL
--存储过程名和参数,参数中in表示传入参数,out标示传出参数,inout表示传入传出参数 create procedure p_procedurecode(in sumdate varchar(1 ...
- [USACO07NOV] Milking Time
题目链接 动态规划转化成 DAG 然后拓扑求解的思路 虽然很简单不过感觉这个新思路会很有用! 如果两个事件互不影响并且有先后关系,就可以连一条有向边,跑最长路可以得到最后的最优解 实际上这还是个背包… ...
- [POI2005] SKA-Piggy Banks
ps.有关Tarjan算法缩点的简要回顾. 今天上午在Luogu随机到了这道题 Luogu P3420,题目大概是这样: 题目描述 Byteazar the Dragon拥有N个小猪存钱罐.每一个存钱 ...
- 4.django学习模板
##引用模板 步骤: 在应用目录下创建templates目录,在目录下创建html文件 在views.py返回render(渲染) 1.requests请求本身,2.模板文件,3.后台传递到前端的数据 ...
- 安装FreeIPA以及应用时报错汇总
安装FreeIPA以及应用时报错汇总 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.ERROR DNS zone yinzhengjie.org.cn already exis ...
- Nginx安装及配置详解包括windows环境
nginx概述 nginx是一款自由的.开源的.高性能的HTTP服务器和反向代理服务器:同时也是一个IMAP.POP3.SMTP代理服务器:nginx可以作为一个HTTP服务器进行网站的发布处理,另外 ...
- Structured Streaming + Kafka Integration Guide 结构化流+Kafka集成指南 (Kafka broker version 0.10.0 or higher)
用于Kafka 0.10的结构化流集成从Kafka读取数据并将数据写入到Kafka. 1. Linking 对于使用SBT/Maven项目定义的Scala/Java应用程序,用以下工件artifact ...
- 上传文件服务与web服务分离
业务场景:1. 后端服务为java web应用,使用tomcat容器,多实例集群化部署.2. 前端使用nginx作为后端应用的反向代理. 业务需求:现在需要在java web应用端上传文件,同时还要能 ...
- ubuntu普通用户使用wireshark的权限问题
解决办法如下: 一.添加wireshark用户组 sudo groupadd wireshark 二.将dumpcap更改为wireshark用户组 sudo chgrp wireshark /usr ...
- Javaweb学习笔记——(十)——————response对象,response字符流缓冲器,响应头,状态码,重定向,requset对象,路径和乱码
请求响应对象: request和response *当服务器接收都请求后,服务器会创建request和response对象,把请求数据封装到request对象中: *然后调用Servlet的sevic ...