案例一
實現一個容器,提供兩個方法,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) + "--------------");
}
}