1.在单处理器时期,操做系统就能处理多线程并发任务,处理器给每一个线程分配CPU时间片,线程在CPU时间片内执行任务linux
2.时间片决定了一个线程能够连续占用处理器运行的时长算法
3.上下文的内容编程
4.当CPU数量远远不止1个的状况下,操做系统将CPU轮流分配给线程任务,此时的上下文切换会变得更加频繁缓存
1.在操做系统中,上下文切换的类型能够分为进程间的上下文切换和线程间的上下文切换bash
2.线程状态:NEW、RUNNABLE、RUNNING、BLOCKED、DEAD多线程
3.线程上下文切换:RUNNING -> BLOCKED -> RUNNABLE -> 被调度器选中执行并发
4.诱因:程序自己触发的自发性上下文切换、系统或虚拟机触发的非自发性上下文切换ide
样例代码高并发
public static void main(String[] args) {
new MultiThreadTesterAbstract().start();
new SerialThreadTesterAbstract().start();
// multi thread take 5401ms
// serial take 692ms
}
static abstract class AbstractTheadContextSwitchTester {
static final int COUNT = 100_000_000;
volatile int counter = 0;
void increaseCounter() {
counter++;
}
public abstract void start();
}
static class MultiThreadTesterAbstract extends AbstractTheadContextSwitchTester {
@Override
public void start() {
Stopwatch stopwatch = Stopwatch.createStarted();
Thread[] threads = new Thread[4];
for (int i = 0; i < 4; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
while (counter < COUNT) {
synchronized (this) {
if (counter < COUNT) {
increaseCounter();
}
}
}
}
});
threads[i].start();
}
for (int i = 0; i < 4; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("multi thread take {}ms", stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
static class SerialThreadTesterAbstract extends AbstractTheadContextSwitchTester {
@Override
public void start() {
Stopwatch stopwatch = Stopwatch.createStarted();
for (int i = 0; i < COUNT; i++) {
increaseCounter();
}
log.info("serial take {}ms", stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
1.串行的执行速度比并发执行的速度要快,由于线程的上下文切换致使了额外的开销工具
2.Redis的设计很好地体现了单线程串行的优点
vmstat
cs:系统的上下文切换频率
root@5d15480e8112:/# vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
3 0 0 693416 33588 951508 0 0 77 154 116 253 1 1 98 0 0
pidstat
-w Report task switching activity (kernels 2.6.23 and later only). The following values may be displayed:
UID
The real user identification number of the task being monitored.
USER
The name of the real user owning the task being monitored.
PID
The identification number of the task being monitored.
cswch/s
Total number of voluntary context switches the task made per second. A voluntary context switch occurs when a task blocks because it requires a
resource that is unavailable.
nvcswch/s
Total number of non voluntary context switches the task made per second. A involuntary context switch takes place when a task executes for the
duration of its time slice and then is forced to relinquish the processor.
Command
The command name of the task.
root@5d15480e8112:/# pidstat -w -l -p 1 2 5
Linux 4.9.184-linuxkit (5d15480e8112) 09/16/2019 _x86_64_ (2 CPU)
07:28:03 UID PID cswch/s nvcswch/s Command
07:28:05 0 1 0.00 0.00 /bin/bash
07:28:07 0 1 0.00 0.00 /bin/bash
07:28:09 0 1 0.00 0.00 /bin/bash
07:28:11 0 1 0.00 0.00 /bin/bash
07:28:13 0 1 0.00 0.00 /bin/bash
Average: 0 1 0.00 0.00 /bin/bash
锁分离
锁分段
能够经过Object对象的wait、notify、notifyAll来实现线程间的通讯,例如生产者-消费者模型
public class WaitNotifyTest {
public static void main(String[] args) {
Vector<Integer> pool = new Vector<>();
Producer producer = new Producer(pool, 10);
Consumer consumer = new Consumer(pool);
new Thread(producer).start();
new Thread(consumer).start();
}
}
@AllArgsConstructor
class Producer implements Runnable {
private final Vector<Integer> pool;
private Integer size;
@Override
public void run() {
for (; ; ) {
try {
produce((int) System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void produce(int i) throws InterruptedException {
while (pool.size() == size) {
synchronized (pool) {
pool.wait();
}
}
synchronized (pool) {
pool.add(i);
pool.notifyAll();
}
}
}
@AllArgsConstructor
class Consumer implements Runnable {
private final Vector<Integer> pool;
@Override
public void run() {
for (; ; ) {
try {
consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void consume() throws InterruptedException {
synchronized (pool) {
while (pool.isEmpty()) {
pool.wait();
}
}
synchronized (pool) {
pool.remove(0);
pool.notifyAll();
}
}
}
1.wait/notify的使用致使了较多的上下文切换
2.消费者第一次申请到锁,却发现没有内容可消费,执行wait,这会致使线程挂起,进入阻塞状态,这是一次上下文切换
3.当生产者得到锁并执行notifyAll以后,会唤醒处于阻塞状态的消费者线程,又会发生一次上下文切换
4.被唤醒的线程在继续运行时,须要再次申请相应对象的内部锁,此时可能须要与其余新来的活跃线程竞争,致使上下文切换
5.若是多个消费者线程同时被阻塞,用notifyAll将唤醒全部阻塞线程,但此时依然没有内容可消费
6.优化方法