题是从网上搜的,便于整理,长期更新。
问题一:如何同时处理多条日志打印
现有的程序代码模拟产生了16个日志对象,并且需要运行16秒才能打印完这些日志,请在程序中增加4个线程去调用parseLog()方法来分头打印这16个日志对象,程序只需要运行4秒即可打印完这些日志对象。原始代码如下:
交给四个线程,打印16个日志对象信息,启动四个线程容易,但是怎样将这16个日志对象交给4个线程,这时候我们用阻塞队列,将要打印的日志信息放到阻塞队列中,四个线程启动时都从阻塞队列中取数据,取完后打印即可。阻塞队列的大小可以自行设置。
修改为多线程打印日志信息后的代码及注释参考下文:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* 多详情并行打印日志
*
* @author Soinice
* @date 2019/5/24 16:35
*/
public class ThreadTestLog {
/**
* 模拟处理16行日志 每秒打印一条 开启4个线程 4秒打印完成
*
* @param args
* @return void
* @author Soinice
* @date 2019/5/24 17:32
*/
public static void main(String[] args) {
System.out.println("begin:" + (System.currentTimeMillis() / 1000));
final BlockingQueue<String> queue = new ArrayBlockingQueue<>(16);
// 启动4个线程
for (int i = 0; i < 4; i++) {
new Thread(new Runnable() {
@Override
public void run() {
// 无限从堵塞队列中取出数据并打印
while (true) {
String log;
try {
log = queue.take();
parseLog(log);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
// 循环16次,打印16个目标对象
for (int i = 0; i < 16; i++) {
final String log = "" + (i + 1);
try {
// 将日志信息放入阻塞队列
queue.put(log);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 解析日志 每一秒输出一条日志
*
* @param log
* @return void
* @author Soinice
* @date 2019/5/24 17:41
*/
private static void parseLog(String log) {
System.out.println(log + ":" + (System.currentTimeMillis() / 1000));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
问题二:如何用多线程实现简单的消费者生产者模式
现成程序中的Test类中的代码在不断地产生数据,然后交给BeforeRun.doSome()方法去处理,就好像生产者在不断地产生数据,消费者在不断消费数据。请将程序改造成有10个线程来消费生成者产生的数据,这些消费者都调用BeforeRun.doSome()方法去进行处理,故每个消费者都需要一秒才能处理完,程序应保证这些消费者线程依次有序地消费数据,只有上一个消费者消费完后,下一个消费者才能消费数据,下一个消费者是谁都可以,但要保证这些消费者线程拿到的数据是有顺序的。原始代码如下:
/**
* 多线程消费
*
* @author Soinice
* @date 2019/5/29 10:34
*/
public class ThreadConsumerTest {
public static void main(String[] args) {
before();
}
/**
* 处理之前
*
* @param
* @return void
* @author Soinice
* @date 2019/5/29 12:04
*/
private static void before() {
System.out.println("begin:" + (System.currentTimeMillis() / 1000));
for (int i = 0; i < 10; i++) {
// 用于生产
String input = i + "";
// 调用消费方法
String output = BeforeRun.doSome(input);
System.out.println(Thread.currentThread().getName() + ":" + output);
}
}
/**
* 消费者在不断消费数据
*
* @author Soinice
* @date 2019/5/29 12:04
*/
static class BeforeRun {
public static String doSome(String input) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String output = input + ":" + (System.currentTimeMillis() / 1000);
return output;
}
}
}
10个线程,依次消费数据,仍然是将待消费数据放入阻塞队列,让10个线程去取数据消费,所不同的是这次消费必须一个一个线程来,而不再是10个线程一起去取,因此用到了线程的同步。同步方法有多种,这里使用Semaphore来进行线程间的同步,代码及注释如下:
public static void main(String[] args) {
// before();
after();
}
/**
* 处理之后
*
* @param
* @return void
* @author Soinice
* @date 2019/5/29 14:06
*/
private static void after() {
final SynchronousQueue<String> queue = new SynchronousQueue<String>();
final Semaphore semaphore = new Semaphore(1);
// 10个线程,分别消费数据,依旧是从阻塞队列中获取数据
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
String input = queue.take();
String output = Run.doSome(input);
System.out.println(Thread.currentThread().getName() + ":" + output);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
System.out.println("begin:" + (System.currentTimeMillis() / 1000));
for (int i1 = 0; i1 < 10; i1++) {
String input = i1 + "";
try {
queue.put(input);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果:
问题三:编写程序实现,子线程循环3次,接着主线程循环5次,接着再子线程循环3次,主线程循环5次,如此反复,循环3次。
第一种实现方式:使用synchronized关键字
/**
* 第一种实现:使用Synchronized关键字
*
* @param
* @return void
* @author lvyimeng
* @date 2019/5/30 10:54
*/
private static void methodOne() {
final ThreadMethodOne threadMethodOne = new ThreadMethodOne();
// 调用子线程循环3次
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
threadMethodOne.subThread();
}
}
}).start();
// 调用主线程循环3次
for (int i = 0; i < 3; i++) {
threadMethodOne.mainThread();
}
}
/**
* 编写功能类,实现主子线程功能
*
* @author lvyimeng
* @date 2019/5/30 10:59
*/
static class ThreadMethodOne {
private boolean flag = false;
/**
* 主线程
*
* @param
* @return void
* @author lvyimeng
* @date 2019/5/30 11:03
*/
public synchronized void mainThread() {
while (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 主线程循环5次
for (int i = 0; i < 5; i++) {
System.out.println("mainThread" + i);
}
this.notify();
flag = false;
}
/**
* 子线程
*
* @param
* @return void
* @author lvyimeng
* @date 2019/5/30 14:42
*/
public synchronized void subThread() {
while (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 子线程循环3次
for (int i = 0; i < 3; i++) {
System.out.println("subThread" + i);
}
this.notify();
flag = true;
}
}
执行结果:
第二种实现方式:使用 lock 锁和 Condition 接口
/**
* 第二种实现:使用 lock 锁和 Condition 接口
*
* @param
* @return void
* @author lvyimeng
* @date 2019/5/30 17:39
*/
private static void methodTwo() {
final ThreadMethodTwo threadMethodTwo = new ThreadMethodTwo();
// 调用子线程循环3次
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
threadMethodTwo.subThread();
}
}
}).start();
// 调用主线程循环3次
for (int i = 0; i < 3; i++) {
threadMethodTwo.mainThread();
}
}
/**
* 编写功能类,实现子线程和主线程的功
*
* @author lvyimeng
* @date 2019/5/30 17:42
*/
static class ThreadMethodTwo extends Thread {
private boolean flag = false;
Lock lock = new ReentrantLock();
Condition con = lock.newCondition();
/**
* 主线程
*
* @param
* @return void
* @author lvyimeng
* @date 2019/5/30 17:46
*/
public void mainThread() {
System.out.println("1:主线程开始 ---- flag=" + flag);
lock.lock();
try {
while (!flag) {
System.out.println("2:主线程等待 ---- flag=" + flag);
try {
// 使当前线程加入 await() 等待队列中,
// 并释放当前锁,当其他线程调用signal()会重新请求锁。与Object.wait()类似
con.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("7.主线程开始循环5次 ---- flag=" + flag);
for (int i = 0; i < 5; i++) {
System.out.println("mainThread()" + i + " ---- flag=" + flag);
}
flag = false;
System.out.println("8.唤醒子线程 ---- flag=" + flag);
// 唤醒一个在 await() 等待队列中的线程,与Object.notify()相似
con.signal();
}
} finally {
lock.unlock();
}
}
/**
* 子线程
*
* @param
* @return void
* @author lvyimeng
* @date 2019/5/30 17:57
*/
public void subThread() {
System.out.println("3.子线程开始 ---- flag=" + flag);
lock.lock();
try {
while (flag) {
System.out.println("6.子线程等待 ---- flag=" + flag);
try {
con.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("4.子线程开始循环3次 ---- flag=" + flag);
for (int i = 0; i < 3; i++) {
System.out.println("subThread()" + i + " ---- flag=" + flag);
}
flag = true;
System.out.println("5.唤醒主线程 ---- flag=" + flag);
con.signal();
} finally {
lock.unlock();
}
}
}
执行结果:
Lock和synchronized的选择
总结来说,Lock和synchronized有以下几点不同:
1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
5)Lock可以提高多个线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
问题四:设计四个线程,其中两个线程每次对变量i加1,另外两个线程每次对i减1。
package cn.com.esgcc.gwtrip.hotel.service.biz;
/**
* 设计四个线程
* 其中两个线程每次对变量i加1,另外两个线程每次对i减1
*
* @author Soinice
* @date 2019/5/30 18:17
*/
public class ThreadCount {
public static void main(String[] args) {
final ThreadMethod threadMethod = new ThreadMethod();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
threadMethod.add();
}
});
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
threadMethod.add();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
threadMethod.sub();
}
});
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
threadMethod.sub();
}
});
thread.start();
thread1.start();
thread2.start();
thread3.start();
}
static class ThreadMethod {
private int i = 0;
/**
* 对变量 i加1
*/
public synchronized void add() {
i++;
System.out.println(Thread.currentThread().getName() + " ---- add():i = " + i);
}
/**
* 对变量 i减1
*/
public synchronized void sub() {
i--;
System.out.println(Thread.currentThread().getName() + " ---- sub():i = " + i);
}
}
}