文章目录
- 一、单元测试Java多线程
- 二、redis实现消息队列
- 三、java多线程模拟生产者消费者
- 四、阻塞读
一、单元测试Java多线程
使用junit测试多线程代码,但是等到程序结束,输出结果不完整,或者是完全没结果,因此,可能是其他线程还没结束,主线程就结束了。
原因: junit在运行时,在主线程结束后就关闭了进程,不会等待各个线程运行结束。
==解决方法:==①要是要求不高,可以通过thread.sleep(),让主线程暂时休眠(TimeUnit.MILLISECONDS.sleep(20000);),其他线程运行完在结束;
②比较严谨的做法,可以用 CountDownLatch ,具体使用在代码里有注释
让主线程休眠,等待其他线程执行完成
public class Learn_Thead {
/**
* 使用主线程休眠的方式来解决:Junit单元测试无法测试多线程问题
* */
@Test
public void runThread() throws InterruptedException {
MyThread m1 = new MyThread("t1");
MyThread m2 = new MyThread("t2");
m1.start();
m2.start();
TimeUnit.MILLISECONDS.sleep(8000);//等待时间
}
}
/**
* 创建新的线程类
*/
class MyThread extends Thread{
private String name;
public MyThread(String name){
this.name = name;
}
public void run(){
for(int i = 0; i < 5; i++){
System.out.println(name + " 运行, i = " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
二、redis实现消息队列
Redis的列表类型可以很容易实现消息队列,类似于MQ的队列模型,任何时候都可以消费消息,但是每一条消息只能允许消费一次。
命令 | 描述 | 用法 |
LPUSH | (1)将一个或多个值value插入到列表key的表头 (2)如果有多个value值,那么各个value值按从左到右的顺序依次插入表头 (3)key不存在,一个空列表会被创建并执行LPUSH操作 (4)key存在但不是列表类型,返回错误 | LPUSH key value [value …] |
LPUSHX | (1)将值value插入到列表key的表头,当且晋档key存在且为一个列表 (2)key不存在时,LPUSHX命令什么都不做 | LPUSHX key value |
LPOP | (1)移除并返回列表key的头元素 | LPOP key |
LRANGE | (1)返回列表key中指定区间内的元素,区间以偏移量start和stop指定 (2)start和stop都以0位底 (3)可使用负数下标,-1表示列表最后一个元素,-2表示列表倒数第二个元素,以此类推 (4)start大于列表最大下标,返回空列表 (5)stop大于列表最大下标,stop=列表最大下标 | LRANGE key start stop |
LREM | (1)根据count的值,移除列表中与value相等的元素 (2)count>0表示从头到尾搜索,移除与value相等的元素,数量为count (3)count<0表示从从尾到头搜索,移除与value相等的元素,数量为count (4)count=0表示移除表中所有与value相等的元素 | LREM key count value |
LSET | (1)将列表key下标为index的元素值设为value (2)index参数超出范围,或对一个空列表进行LSET时,返回错误 | LSET key index value |
LINDEX | (1)返回列表key中,下标为index的元素 | LINDEX key index |
LINSERT | (1)将值value插入列表key中,位于pivot前面或者后面 (2)pivot不存在于列表key时,不执行任何操作(3)key不存在,不执行任何操作 | LINSERT key BEFORE|AFTER pivot value |
LLEN | (1)返回列表key的长度(2)key不存在,返回0 | LLEN key |
LTRIM | (1)对一个列表进行修剪,让列表只返回指定区间内的元素,不存在指定区间内的都将被移除 | LTRIM key start stop |
RPOP | (1)移除并返回列表key的尾元素 | RPOP key |
RPOPLPUSH | 在一个原子时间内,执行两个动作:(1)将列表source中最后一个元素弹出并返回给客户端 (2)将source弹出的元素插入到列表desination,作为destination列表的头元素 | RPOPLPUSH source destination |
RPUSH | (1)将一个或多个值value插入到列表key的表尾 | RPUSH key value [value …] |
RPUSHX | (1)将value插入到列表key的表尾,当且仅当key存在并且是一个列表 (2)key不存在,RPUSHX什么都不做 | RPUSHX key value |
- 栈 通过命令 lpush+lpop
- 队列 命令 lpush+rpop
- 有限集合 命令 lpush+ltrim
- 消息队列 命令 lpush+rpop
127.0.0.1:6379> lpush list a b c d
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
127.0.0.1:6379> rpop list
"a"
127.0.0.1:6379> rpop list
"b"
三、java多线程模拟生产者消费者
RedisLearn.java
/**
* 生产者类
* 生产者每隔600ms生成一条消息
* */
class MessageProducer extends Thread{
public static final String MESSAGE_KEY = "message:queue";
private volatile int count;
public void putMessage(String mess){
Jedis jedis = new Jedis("IP", 6379);
jedis.auth("123456");
Long size = jedis.lpush(MESSAGE_KEY, mess);
System.out.println("Put " + Thread.currentThread().getName() + " put message " + count);
count++;
}
@Override
public synchronized void run() {
for(int i = 0 ; i < 5; i++){
putMessage("message" + count);
try {
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 消费者类
* 消费者每隔1000ms消费一条消息
*/
class MessageConsumer extends Thread{
public static final String MESSAGE_KEY = "message:queue";
private volatile int count;
public void consumerMessage(){
Jedis jedis = new Jedis("IP", 6379);
jedis.auth("123456");
String message = jedis.rpop(MESSAGE_KEY);
System.out.println("Pop " + Thread.currentThread().getName() + "comsumer message = " + message);
count++;
}
@Override
public synchronized void run() {
for(int i = 0; i < 9; i++){
consumerMessage();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RedisLearn {
/**
* 使用单元测试,测试多线程下模拟生产者消费者
* 使用Redis List : lpush rpop 模拟消息队列
* @throws InterruptedException
*/
@Test
public void runMessageProducter() throws InterruptedException {
MessageProducer producer = new MessageProducer();
MessageConsumer comsumer = new MessageConsumer();
Thread t1 = new Thread(producer, "thread1");
Thread t2 = new Thread(producer, "thread2");
Thread t3 = new Thread(producer, "thread3");
Thread t4 = new Thread(comsumer, "thread4");
Thread t5 = new Thread(comsumer, "thread5");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
TimeUnit.MILLISECONDS.sleep(20000);
}
}
result
Put thread1 put message 0
Pop thread5comsumer message = message0
Put thread1 put message 1
Pop thread5comsumer message = message1
Put thread1 put message 2
Pop thread5comsumer message = message2
Put thread1 put message 3
Put thread1 put message 4
Pop thread5comsumer message = message3
Put thread2 put message 5
Put thread2 put message 6
Pop thread5comsumer message = message4
Put thread2 put message 7
Pop thread5comsumer message = message5
Put thread2 put message 8
Put thread2 put message 9
Pop thread5comsumer message = message6
Put thread3 put message 10
Pop thread5comsumer message = message7
Put thread3 put message 11
Put thread3 put message 12
Pop thread5comsumer message = message8
Put thread3 put message 13
Pop thread4comsumer message = message9
Put thread3 put message 14
Pop thread4comsumer message = message10
Pop thread4comsumer message = message11
Pop thread4comsumer message = message12
Pop thread4comsumer message = message13
Pop thread4comsumer message = message14
Pop thread4comsumer message = null
Pop thread4comsumer message = null
Pop thread4comsumer message = null
四、阻塞读
Redis Brpop 命令移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
/**
* 消费者类
*/
class MessageConsumer extends Thread{
public static final String MESSAGE_KEY = "message:queue";
public void consumerMessage(){
Jedis jedis = new Jedis("nuptpisa.top", 6379);
jedis.auth("123456");
// String message = jedis.rpop(MESSAGE_KEY);
// System.out.println("Pop " + Thread.currentThread().getName() + "comsumer message = " + message);
List<String> message = jedis.brpop(0, MESSAGE_KEY);
System.out.println("Pop " + Thread.currentThread().getName() + "comsumer message = " + message);
}
@Override
public synchronized void run() {
for(int i = 0; i < 8; i++){
consumerMessage();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
result
Put thread1 put message 0
Pop thread4comsumer message = [message:queue, message0]
Put thread1 put message 1
Pop thread4comsumer message = [message:queue, message1]
Put thread1 put message 2
Put thread1 put message 3
Pop thread4comsumer message = [message:queue, message2]
Put thread1 put message 4
Pop thread4comsumer message = [message:queue, message3]
Put thread3 put message 5
Put thread3 put message 6
Pop thread4comsumer message = [message:queue, message4]
Put thread3 put message 7
Pop thread4comsumer message = [message:queue, message5]
Put thread3 put message 8
Put thread3 put message 9
Pop thread4comsumer message = [message:queue, message6]
Put thread2 put message 10
Pop thread4comsumer message = [message:queue, message7]
Put thread2 put message 11
Put thread2 put message 12
Pop thread5comsumer message = [message:queue, message8]
Put thread2 put message 13
Pop thread5comsumer message = [message:queue, message9]
Put thread2 put message 14
Pop thread5comsumer message = [message:queue, message10]
Pop thread5comsumer message = [message:queue, message11]
Pop thread5comsumer message = [message:queue, message12]
Pop thread5comsumer message = [message:queue, message13]
Pop thread5comsumer message = [message:queue, message14]
Process finished with exit code 0