布景常识

  1. 软件是怎么驱动硬件的?
    硬件是需要相关的驱动程序才能履行,而驱动程序是安装在操作体系内核中。如果写了一个程序A,A程序想操作硬件工作,首要需要进行体系调用,由内核去找对应的驱动程序唆使硬件工作。而驱动程序怎么让硬件工作的呢?驱动程序作为硬件和操作体系之间的媒介,能够把操作体系中相关的指令翻译成硬件能够识别的电信号,同时,驱动程序也能够将硬件的电信号转为操作体系能够识别的指令。
  2. 进程、轻量级进程、线程联系
    一个进程因为所运转的空间不同,被分为内核线程和用户进程,之所有称之为内核线程,是因为其不拥有虚拟地址空间。如果创立一个新的用户进程,会分配一个新的虚拟地址空间,不同用户进程之间资源是阻隔的。因为创立一个新的进程需要消耗许多的资源,而且在进程之间切换的价值也很贵重,因此引入了轻量级进程。轻量级进行本质上也是对内核线程的高层抽象,虽然不同的轻量级进程之间能够共享某些资源,但因为轻量级进程本质上仍是内核线程,如果进行轻量级线程之间的切换,需要进行体系调用,价值也是比较贵重的。内核本质上只能感知到进程的存在,像不同言语的多线程技能,是在用户进程的基础上创立的线程库,线程自身不参加处理器竞赛,而是由其所属的用户进程参加处理器的竞赛。
  3. 怎么了解用户态和内核态
    首要咱们需要了解到计算机资源是有限的,不管是CPU资源、内存资源、IO资源、网络资源,为了确保这些资源的合理运用,需要有一个管控机制,而这个管控机制都是交于操作体系来处理的。用户态和内核态是操作体系的一种逻辑划分,本质上是进行权限操控,处于用户态的进程能够直接运用分配给其的内存空间,但如果想运用CPU等稀缺资源,处于用户态的进程就没有这个权限了,有必要通过体系调用,让当时进程进入内核态,这样能够有更大的权限去申请CPU资源、内存资源、IO资源等;

操作体系线程模型

常用语言的线程模型(Java、go、C++、python3) | 京东云技术团队

java言语

线程模型

Java诞生之初,在Java中就引入了线程,开始称之为“绿色线程”,完全由JVM进行管理,这和操作体系用户线程是多对一的完成,但随着操作体系对线程支撑越来越强壮,java中的线程完成选用了一对一的完成,即一个java线程对应于一个操作体系用户线程,可是这个线程的堆栈巨细是固定的,随着线程数量创立过多,或许导致内存溢出。在java19版本中引入了虚拟线程的概念,虚拟线程有一个动态的堆栈,能够增大和缩小,这和操作体系用户线程之间是一个多对多的联系,随着后面的发展,java中的线程模型会变得越来越强壮。

常用语言的线程模型(Java、go、C++、python3) | 京东云技术团队

优缺陷

作为一对一的线程模型保护起来比较简单,可是因为每一个线程栈信息是固定的,不利于创立很多的线程,而且多线程操作时或许触及频频的体系调用,上下文切换价值高。

运用方法(以出产者顾客模型来阐明)

 public class ThreadTest {
    public static final Object P = new Object();
    static List<Integer> list = new ArrayList<>();
    @Test
    public void test() throws Exception {
        Thread thread1 = new Thread(()-> {
            while(true) {
                try {
                    product();
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            while(true) {
                try {
                    consume();
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
    }
    private static void product() throws Exception {
        synchronized (P) {
            if(list.size() == 1) {
                // 让出锁
                P.wait();
            }
            list.add(1);
            System.out.println("produce");
            P.notify();
        }
    }
    private static void consume() throws Exception {
        synchronized (P) {
            if(list.size() == 0) {
                P.wait();
            }
            list.remove(list.size() - 1);
            System.out.println("consume");
            P.notify();
        }
    }
}

go言语

go言语线程模型

在go言语中,线程模型便是比较强壮了,包含了三个概念:内核线程(M)、goroutine(G)、G的上下文环境(P)。其中G表示基于协程创立的用户线程,M直接相关一个内核线程,P里面一般存放正在运转的goroutine的上下文环境(函数指针、堆栈地址和地址边界等)。

常用语言的线程模型(Java、go、C++、python3) | 京东云技术团队

优缺陷

go言语中的线程模型算是很强壮了,引用了协程,线程栈巨细能够动态调整,很好地避免了java中目前的线程模型缺陷。

运用方法(以出产者顾客模型来阐明)

package main
import (
	"fmt"
)
type ThreadTest struct {
	lock chan int
}
func (t *ThreadTest) produce() {
	for {
		t.lock <- 10
		fmt.Println("produce:", 10)
	}
}
func (t *ThreadTest) consume() {
	for {
		v := <-t.lock
		fmt.Println("consume:", v)
	}
}
func main() {
	maxLen := 10
	t := &ThreadTest{
		make(chan int, maxLen),
	}
	// 重点在这里,敞开新的协程,配合通道,让go的多线程变成十分高雅
	go t.consume()
	go t.produce()
	select {}
}

c++言语

c++言语线程模型

在c++11中增加了操作thread库,提供对线程操作的进一步封装,而这个库底层是运用了pthread库,这个库底层选用了1:1线程模型,跟java中的线程模型相似。

优缺陷

作为一对一的线程模型保护起来比较简单,可是因为每一个线程栈信息是固定的,不利于创立很多的线程,而且多线程操作时或许触及频频的体系调用,上下文切换价值高。

运用方法(以出产者顾客模型来阐明)

#include 
#include 
#include 
#include  
static const int SIZE = 10;
static const int ITEM_SIZE = 30;
std::mutex mtx;
std::condition_variable not_full;
std::condition_variable not_empty;
int items[SIZE];
static std::size_t r_idx = 0;
static std::size_t w_idx = 0;
void produce(int i) {
    std::unique_lock lck(mtx);
    while((w_idx+ 1) % SIZE == r_idx) {
        std::cout << "队列满了" << std::endl;
        not_full.wait(lck);
    }
    items[w_idx] = i;
    w_idx = (w_idx+ 1) % SIZE;
    not_empty.notify_all();
    lck.unlock();
}
int consume() {
    int data;
    std::unique_lock lck(mtx);
    while(w_idx == r_idx) {
        std::cout << "队列为空" << std::endl;
        not_empty.wait(lck);
    }
    data = items[r_idx];
    r_idx = (r_idx + 1) % SIZE;
    not_full.notify_all();
    lck.unlock();
    return data;
}
void p_t() {
    for(int i = 0; i < ITEM_SIZE; i++) {
        produce(i);
    }
}
void c_t() {
    static int cnt = 0;
    while(1) {
        int item = consume();
        std::cout << "消费第" << item << "个商品" << std::endl;
        if(++cnt == ITEM_SIZE) {
            break;
        }
    }
}
int main() {
    std::thread producer(p_t);
    std::thread consumer(c_t);
    producer.join();
    consumer.join();
}

python言语

python线程模型

python中的线程运用了操作体系的原生线程,python虚拟机运用了一个全局互斥锁(GIL)来互斥线程对Python虚拟机的运用,当一个线程获取GIL的权限之后,其他的线程有必要等候这个线程开释GIL锁,索引再多核CPU上,python多线程也会退化为单线程,无法运用多核的优势。

常用语言的线程模型(Java、go、C++、python3) | 京东云技术团队

优缺陷

python言语多线程因为GIL的存在,在计算密集型场景上,很难体现到优势,而且因为触及线程切换的代码,反而或许性能还不如单线程好。

运用方法(以出产者顾客模型来阐明)

#! /usr/bin/python3
import threading
import random
import time
total = 100
lock = threading.Lock()
totalTime = 10
gTime = 0
class Consumer(threading.Thread):
        def run(self):
                global total
                global gTime
                while True:
                        cur = random.randint(10, 100)
                        lock.acquire()
                        if total >= cur:
                                total -= cur
                                print("{}运用了{}, 当时剩下{}".format(threading.current_thread(), cur, total))
                        else:
                            print("{}预备运用{},当时剩下{},缺乏,不能消费".format(threading.current_thread(), cur, total))
                        if gTime == totalTime:
                               lock.release()
                               break
                        lock.release()
                        time.sleep(0.7)
class Producer(threading.Thread):
    def run(self):
           global total
           global gTime
           while True:
                  cur = random.randint(10, 100)
                  lock.acquire()
                  if gTime == totalTime:
                         lock.release()
                         break
                  total += cur
                  print("{}出产了{}, 剩下{}".format(threading.current_thread(), cur, total))
                  gTime+= 1
                  lock.release()
                  time.sleep(0.5)
if __name__ == '__main__':
       t1 = Producer(name="出产者")
       t1.start()
       t2 = Consumer(name="顾客")
       t2.start()

总结

在目前的线程模型中,有1:1、M:1、M:N多种线程模型,具体选用哪种线程模型也和硬件和操作体系的支撑程度有关,像诞生比较早的言语,普通选用M:1、1:1线程模型,像c++、java。而新诞生不久的go言语,选用的是M:N线程模型,在多线程的支撑上更加强壮。

感觉了解一下线程模型仍是很有必要的,如果不清楚言语层面上的线程在操作体系层面怎么映射运用,在运用过程中就会不清不楚,或许会踩一些坑,咱们都知道在java中不同无限的创立线程,这会导致内存溢出,go言语中对多线程支撑更加强壮,许多事情不需要咱们再去重视了,在言语底层现已帮助咱们做了。

每种言语的底层细节太多了,如果想深入研究某一个技能,仍是得花精力去研究。

作者:京东零售 姜昌伟

来历:京东云开发者社区