Java多线程经典案例分享
案例一
实现一个容器,提供两个方法,add(),count() 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束。
本案例我通过闭锁(也叫门栓锁)实现,实现如下:
package day_12_28.zuoye;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* @author soberw
* @Classname AddAndCount
* @Description 实现一个容器,提供两个方法,add,count 写两个线程,
* 线程1添加10个元素到容器中,线程2实现监控元素的个数,
* 当个数到5个时,线程2给出提示并结束。
* @Date 2021-12-28 10:45
*/
public class AddAndCount {
CountDownLatch cdl = new CountDownLatch(1);
List<String> list = new ArrayList<>();
public static void main(String[] args) {
var aac = new AddAndCount();
new Thread(aac::add, "A").start();
new Thread(aac::count, "B").start();
}
void add() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
String item = String.format("%s - %d", "item", i);
list.add(item);
System.out.println(Thread.currentThread().getName() + ":" + item);
if (i == 4) {
cdl.countDown();
}
}
}
void count() {
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("程序结束...");
System.exit(0);
}
}
案例二
编写程序模拟死锁。
死锁,简单来说就是两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
下面我就模拟这一状态:
package day_12_28.zuoye;
/**
* @author soberw
* @Classname Deadlock
* @Description 编写程序模拟死锁
* @Date 2021-12-28 10:59
*/
public class Deadlock {
private final Object o1 = new Object();
private final Object o2 = new Object();
public static void main(String[] args) {
Deadlock d = new Deadlock();
new Thread(d::m1).start();
new Thread(d::m2).start();
}
void m1(){
System.out.println(Thread.currentThread().getName() + "启动等待...");
synchronized(o1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(o2){
System.out.println("哈哈..");
}
}
}
void m2(){
System.out.println(Thread.currentThread().getName() + "启动等待...");
synchronized(o2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(o1){
System.out.println("哈哈..");
}
}
}
}
案例三
编写程序,实现三个线程,运行输出 A1 B2 C3 A4 B5 C6 ……
我这里用了两种方式去实现:
方式一:
用公平锁:
package day_12_28.zuoye;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author soberw
* @Classname TurnNumber
* @Description 编写程序,实现三个线程,运行输出 A1 B2 C3 A4 B5 C6 ….. 用公平锁
* @Date 2021-12-28 14:09
*/
public class TurnNumber {
AtomicInteger num = new AtomicInteger(0);
private final ReentrantLock rl = new ReentrantLock(true);
public void show() {
for (; ; ) {
rl.lock();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
String tn = Thread.currentThread().getName();
int i = num.incrementAndGet();
String s = String.format("%s%d", tn, i);
System.out.print(s + " ");
if ("C".equals(tn)) {
System.out.println();
}
rl.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
TurnNumber tn = new TurnNumber();
Thread a = new Thread(tn::show, "A");
Thread b = new Thread(tn::show, "B");
Thread c = new Thread(tn::show, "C");
a.setPriority(Thread.MAX_PRIORITY);
a.start();
b.setPriority(Thread.NORM_PRIORITY);
b.start();
c.setPriority(Thread.MIN_PRIORITY);
c.start();
}
}
方式二:
用join() 方法
package day_12_28.zuoye;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author soberw
* @Classname TurnNumber
* @Description 编写程序,实现三个线程,运行输出 A1 B2 C3 A4 B5 C6 ….. 第二种写法,用join()
* @Date 2021-12-28 14:09
*/
public class TurnNumber2 {
AtomicInteger num = new AtomicInteger(0);
public void show() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
String tn = Thread.currentThread().getName();
int i = num.incrementAndGet();
String s = String.format("%s%d", tn, i);
System.out.print(s + " ");
if ("C".equals(tn)) {
System.out.println();
}
}
public static void main(String[] args) throws InterruptedException {
TurnNumber2 tn = new TurnNumber2();
while (true) {
Thread a = new Thread(tn::show, "A");
Thread b = new Thread(tn::show, "B");
Thread c = new Thread(tn::show, "C");
a.setPriority(Thread.MAX_PRIORITY);
a.start();
a.join();
b.setPriority(Thread.NORM_PRIORITY);
b.start();
b.join();
c.setPriority(Thread.MIN_PRIORITY);
c.start();
c.join();
}
}
}
案例四
创建五个线程并进入等待状态,等两秒后主线程开始并释放全部线程,最后主线程结束
本案例我用的是wait() 与notifyAll()组合形式;
package day_12_27;
import java.util.concurrent.TimeUnit;
/**
* @author soberw
* @Classname WaitAndNotify
* @Description 创建五个线程并进入等待状态,等两秒后主线程开始并释放全部线程,最后主线程结束
* @Date 2021-12-27 15:47
*/
public class WaitAndNotify {
public static void main(String[] args) {
Object co = new Object();
for (int i = 0; i < 5; i++) {
MyThread t = new MyThread("Thread" + i, co);
t.start();
}
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("-----Main Thread notify-----");
synchronized (co) {
//co.notify();
co.notifyAll();
}
TimeUnit.SECONDS.sleep(2);
System.out.println("Main Thread is end.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class MyThread extends Thread {
private String name;
private Object co;
public MyThread(String name, Object o) {
this.name = name;
this.co = o;
}
@Override
public void run() {
System.out.println(name + " is waiting.");
try {
synchronized (co) {
co.wait();
}
System.out.println(name + " has been notified.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
案例五
用五个线程实现,求123456789 之间放±和100的表达式,如果一个线程求出结果,立即告诉其它停止。
这里我用到了AtomicBoolean原子类来保证数据的原子性:
package day_12_27.zuoye;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author soberw
* @Classname Number100
* @Description 用五个线程实现,求123456789 之间放+-和100的表达式,如果一个线程求出结果,立即告诉其它停止。
* @Date 2021-12-27 21:14
*/
public class Number100 {
//原子类,保证原子性
AtomicBoolean ab = new AtomicBoolean(true);
public void show() {
String[] ss = {"", "+", "-"};
StringBuilder sbu = new StringBuilder();
sbu.append("1");
Random random = new Random();
while (ab.get()) {
for (int i = 2; i < 9; i++) {
sbu.append(ss[random.nextInt(3)]);
sbu.append(i);
}
Pattern p = Pattern.compile("[0-9]+|-[0-9]+");
Matcher m = p.matcher(sbu.toString());
int sum = 0;
while (m.find()) {
sum += Integer.parseInt(m.group());
}
if (sum == 100) {
ab.set(false);
System.out.println(Thread.currentThread().getName() + ":" + sbu.toString() + " = 100");
}
sbu.delete(1, sbu.length());
}
}
public static void main(String[] args) {
var n = new Number100();
for (int i = 0; i < 5; i++) {
new Thread(n::show).start();
}
}
}
案例六
模拟经典问题,生产者-消费者问题:
方式一:
package day_12_28.zuoye;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author soberw
* @Classname ProductorAndConsumerForLock
* @Description 用线程通信机制解决生产者消费者问题
* @Date 2021-12-28 19:18
*/
public class ProductorAndConsumerForLock {
public static void main(String[] args) {
Clerk1 clerk1 = new Clerk1();
Productor1 pro = new Productor1(clerk1);
Consumer1 con = new Consumer1(clerk1);
new Thread(pro, "生产者 A").start();
new Thread(con, "消费者 B").start();
// new Thread(pro, "生产者 C").start();
// new Thread(con, "消费者 D").start();
}
}
class Clerk1 {
private int product = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
// 进货
public void get() {
lock.lock();
try {
if (product >= 1) { // 为了避免虚假唤醒,应该总是使用在循环中。
System.out.println("产品已满!");
try {
condition.await();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : "
+ ++product);
condition.signalAll();
} finally {
lock.unlock();
}
}
// 卖货
public void sale() {
lock.lock();
try {
if (product <= 0) {
System.out.println("缺货!");
try {
condition.await();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : "
+ --product);
condition.signalAll();
} finally {
lock.unlock();
}
}
}
// 生产者
class Productor1 implements Runnable {
private Clerk1 clerk1;
public Productor1(Clerk1 clerk1) {
this.clerk1 = clerk1;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk1.get();
}
}
}
// 消费者
class Consumer1 implements Runnable {
private Clerk1 clerk1;
public Consumer1(Clerk1 clerk1) {
this.clerk1 = clerk1;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk1.sale();
}
}
}
方式二:
package day_12_28.zuoye;
/**
* @author soberw
* @Classname ProducerAndConsumer
* @Description 用等待唤醒机制解决生产者消费者问题
* @Date 2021-12-28 16:25
*/
public class ProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Consumer cus = new Consumer(clerk);
new Thread(pro, "生产者 A").start();
new Thread(cus, "消费者 B").start();
new Thread(pro, "生产者 C").start();
new Thread(cus, "消费者 D").start();
}
}
//店员
class Clerk {
private int product = 0;
//进货
public synchronized void get() {//循环次数:0
//为了避免虚假唤醒问题,应该总是使用在循环中
while (product >= 1) {
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : " + ++product);
this.notifyAll();
}
//卖货
public synchronized void sale() {//product = 0; 循环次数:0
while (product <= 0) {
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : " + --product);
this.notifyAll();
}
}
//生产者
class Productor implements Runnable {
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
clerk.get();
}
}
}
//消费者
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();
}
}
}
案例七
开十个线程打印输出1~10000中偶数的值,计算总耗时
我采用的是闭锁机制:
package day_12_28.zuoye;
import java.util.concurrent.CountDownLatch;
/**
* @author soberw
* @Classname CountTime
* @Description 开十个线程打印输出1~10000中偶数的值,计算总耗时 用闭锁(门栓)
* @Date 2021-12-28 15:22
*/
public class CountTime {
static CountDownLatch cdl = new CountDownLatch(10);
void show() {
for (int i = 0; i < 10000; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
cdl.countDown();
}
public static void main(String[] args) {
CountTime ct = new CountTime();
long start = System.currentTimeMillis();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
new Thread(ct::show).start();
}
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println((end - start - 100) + "--------------");
}
}