举个多孩家庭的例子(响应国家号召)
vmstat
vmstat
命令可以测量上下文切换的次数vmstat 1
,表示每隔1秒打印系统的状态
减少上下文切换的方法
compare and swap
,内存地址V中的值,与旧的预期值A一致,则更新为新的值B;否则,什么都不做为何说说Java线程与OS的线程是一一对应的?
start()
方法中,调用native的start0()
方法,最终会在OS级别创建一个线程1:1
的关系go语言中的协程
go语言从语言层面原生支持协程,这是其一大特色
下面是一段协程的代码:
go
语言关键字,以协程的方式执行函数say()
WaitGroup
避免由于主线程结束,导致协程还未执行就结束thread.join()
有异曲同工的作用package main
import (
"sync"
)
var wg = sync.WaitGroup{}
func main() {
wg.Add(1)
go say("Hello World")
wg.Wait()
}
func say(s string) {
println(s)
wg.Done()
}
Java是否支持协程?
Kotlin
,Kotlin-JVM这个组合并不支持协程参考文档
死锁的官方定义:
简单的说
当前进程拥有其他进程等待的资源
当前进程等待其他进程已拥有的资源
所有进程都不放弃自己拥有的资源
死锁的原因:① 由于资源不足而引发的资源竞争;② 并发执行的顺序不当
死锁的四个必要条件:互斥、占有且等待、不可抢占、循环等待
基于synchronized关键字,实现一个死锁的示例程序
public class DeadLock {
public static String objA = "objA";
public static Integer objB = 1;
public static void main(String[] arg) {
// 创建线程Thread1
Thread thread1 = new Thread(() -> {
try {
System.out.println("Thread1 is running ...");
synchronized (DeadLock.objA) { // Thread1先获取objA
System.out.printf("%s got the DeadLock.objA\n", Thread.currentThread().getName());
Thread.sleep(1000); // 等待Thread2获取资源objB
synchronized (DeadLock.objB) { // Thread1再获取objB
System.out.printf("%s got the DeadLock.objB\n", Thread.currentThread().getName());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.setName("Thread1");
// 创建线程Thread2
Thread thread2 = new Thread(() -> {
try {
System.out.println("Thread2 is running ...");
synchronized (DeadLock.objB) { // Thread2先获取objB
System.out.printf("%s got the DeadLock.objB\n", Thread.currentThread().getName());
Thread.sleep(1000); // 等待Thread1获取资源objA
synchronized (DeadLock.objA) { // Thread2再获取objA
System.out.printf("%s got the DeadLock.objA\n", Thread.currentThread().getName());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread2.setName("Thread2");
// 启动线程,出现死锁,无法退出程序
thread1.start();
thread2.start();
}
}
通过jps
命令,获取当前程序的进程号:12218
通过jstack命令jstack 12218
,获取进程的dump信息:
从教科书层面来说,死锁的解决办法有
胃口小
的客户,该客户生意成功后就能归还所有的资金;问题描述:
synchronized
、wait
、notify
或notifyAll
就可以实现上述的同步要求解决办法分析:
实质: 有博客说这是破坏了循环等待,但是自己觉得好像和破坏循环等待的方法套不上?(欢迎交流)
使用信号量可以解决该问题
信号量:实质是PV操作
value >= 0
,表明条件满足,可以进入临界区;否则,线程会被阻塞,直到被其他线程唤醒value <= 0
,表明有线程在等待,需要唤醒一个阻塞的线程,然后退出临界区;否则,直接退出临界区。permits
,
基于信号量的代码实现
public class PhilosopherProblem {
// 5把叉子,每把的状态均为可用或不可用
static Semaphore[] forks = new Semaphore[5];
// 可以拿起左边叉子的人数
static Semaphore leftCount;
public static void main(String[] arg) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
// 初始化信号量
for (int i = 0; i < 5; i++) {
forks[i] = new Semaphore(1, true);
}
leftCount = new Semaphore(4, true);
// 创建5个哲学家线程,模拟哲学家思考、吃面的过程
for (int i = 0; i < 5; i++) {
executorService.submit(new Philosopher(i));
}
}
// 通过信号量实现哲学家的吃面过程
static class Philosopher implements Runnable {
private int index;
public Philosopher(int index) {
this.index = index;
}
@Override
public void run() {
while (true) {
try {
// 哲学家思考
System.out.println("Philosopher " + index + ": I'm thinking");
Thread.sleep(1000);
// 哲学家尝试拿起叉子
leftCount.acquire(); // 要求拿起左边叉子的人数最大为4,否则需要等待
forks[index].acquire(); // 尝试拿起左边的叉子
forks[(index + 1) % 5].acquire(); // 尝试拿起右边的叉子
// 开始吃面
System.out.println("Philosopher " + index + ": I'm eating");
Thread.sleep(1000);
// 放下叉子
forks[index].release();
leftCount.release(); // 左边的叉子成功放下,拿起左边叉子的人数减一
forks[(index + 1) % 5].release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
还有一种很奇妙的解决办法:给哲学家编号:1 ~ 5,奇数号的哲学家先拿起左边的叉子,偶数号的哲学家先拿起右手边的叉子
形成如图所示的场景:
还是可以使用信号量实现:每把叉子对应值为1的信号量,按照哲学家的编号,决定应该先拿起哪边的叉子
public class PhilosopherProblem {
// 5把叉子,每把的状态均为可用或不可用
static Semaphore[] forks = new Semaphore[5];
public static void main(String[] arg) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
// 初始化信号量
for (int i = 0; i < 5; i++) {
forks[i] = new Semaphore(1, true);
}
// 创建5个哲学家线程,模拟哲学家思考、吃面的过程
for (int i = 0; i < 5; i++) {
executorService.submit(new Philosopher(i));
}
}
// 通过信号量实现
static class Philosopher implements Runnable {
private int index;
public Philosopher(int index) {
this.index = index;
}
@Override
public void run() {
while (true) {
try {
// 哲学家思考
System.out.println("Philosopher " + index + ": I'm thinking");
Thread.sleep(1000);
// 根据编号,决定拿起叉子的顺序
if (index % 2 == 0) { // 奇数号哲学家,先左手,再右手
forks[index].acquire();
forks[(index+1)%5].acquire();
} else { // 偶数号哲学家,先右手,再左手
forks[(index + 1) % 5].acquire();
forks[index].acquire();
}
// 开始吃面
System.out.println("Philosopher " + index + ": I'm eating");
Thread.sleep(1000);
// 放下叉子
if (index % 2 == 0) {
forks[index].release();
forks[(index+1)%5].release();
} else {
forks[(index + 1) % 5].release();
forks[index].release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
参考链接
并发度越大,程序运行的越快或效率越高?
常见的资源限制
如何解决资源限制的问题?
并发编程,学会因地制宜,及时根据资源限制调整并发度