Java基础-线程和进程(一)

2021-04-20  本文已影响0人  涛涛123759

Android知识总结

一、什么是进程和线程

1)、进程是程序运行资源分配的最小单位
2)、线程是 CPU 调度的最小单位,必须依赖于进程而存在
3)、线程无处不在
4)、线程并发的好处

二、 CPU核心数和线程数的关系

多核心:也指单芯片多处理器( Chip Multiprocessors,简称 CMP),CMP 是由美国斯坦福大学提出的,其思想是将大规模并行处理器中的 SMP(对称多处理器)集成到同一芯片内,各个处理器并行执行不同的进程。这种依靠多个 CPU 同时并行地运行程序是实现超高速计算的一个重要方向,称为并行处理。

多线程:Simultaneous Multithreading.简称 SMT.让同一个处理器上的多个线程同步执行并共享处理器的执行资源。

  • 核心数、线程数:目前主流 CPU 都是多核的。增加核心数目就是为了增加线程数,因为操作系统是通过线程来执行任务的,一般情况下它们是 1:1 对应关系,也就是说四核 CPU 一般拥有四个线程。但 Intel 引入超线程技术后,使核心数与线程数形成 1:2 的关系。

创建最大线程数的限制(OS):Linux 最大 1000个,Window最大2000个

三、Thread、Runable

线程状态图

1)、两种启动方式
启动线程的方式有:

2)、Thread 和 Runnable 的区别

start 只能调用一次,并创建一个子线程,run可以被多次调用。run通俗的说就是Thread里面的成员方法,与线程的启动没有关系。只有start才能启动一个线程。

object ThreadUtil {
    class MyThread : Thread(){
        override fun run() {
            super.run()
            println(Thread.currentThread().name)
        }
    }

    @JvmStatic
    fun main(argc : Array<String>){
        val myThread = MyThread()
        myThread.name = "My Thread"
        myThread.run()  //调用的是 main 线程
        myThread.start() //只能调运一次, 否则报 IllegalThreadStateException异常
        Thread.sleep(1000)
        myThread.run()  //调用的是 main 线程
    }
}

3)、线程中断
安全的中止则是其他线程通过调用某个线程 A 的 interrupt()方法对其进行中断操作, 中断好比其他线程对该线程打了个呼,“A,你要中断了”,不代表线程 A 会立即停止自己的工作,同样的 A 线程完全可以不理会这种中断请求。因为 java 里的线程是协作式的,不是抢占式的。线程通过检查自身的中断标志位是否被置为 true 来进行响应。

程通过方法isInterrupted()来进行判断是否被中断,也可以调用静态方法Thread.interrupted()来进行判断当前线程是否被中断,不过 Thread.interrupted()会同时将中断标识位改写为 false

如果一个线程处于了阻塞状态(如线程调用了 thread.sleep、thread.join、thread.wait 等),则在线程在检查中断标示时如果发现中断标示为 true,则会在这些阻塞方法调用处抛出InterruptedException异常,并且在抛出异常后会立即将线程的中断标示位清除,即重新设置为 false。

注意:处于死锁状态的线程无法被中断

四、线程上下问切换

线程上下文是指某一时间点 CPU 寄存器程序计数器的内容,CPU通过时间片分配算法来循环执行任务(线程),因为时间片非常短,所以CPU通过不停地切换线程执行。

上下文切换

  • 线程切换,同一进程中的两个线程之间的切换
  • 进程切换,两个进程之间的切换
  • 模式切换,在给定线程中,用户模式和内核模式的切换
  • 地址空间切换,将虚拟内存切换到物理内存

CPU切换前把当前任务的状态保存下来,以便下次切换回这个任务时可以再次加载这个任务的状态,然后加载下一任务的状态并执行。任务的状态保存及再加载, 这段过程就叫做上下文切换。

  • 每个线程都有一个程序计数器记录要执行的下一条指令),一组寄存器保存当前线程的工作变量),堆栈(记录执行历史,其中每一帧保存了一个已经调用但未返回的过程)。
  • 寄存器 是 CPU 内部的数量较少但是速度很快的内存(与之对应的是 CPU 外部相对较慢的 RAM 主内存)。寄存器通过对常用值(通常是运算的中间值)的快速访问来提高计算机程序运行的速度。
  • 程序计数器是一个专用的寄存器,用于表明指令序列中 CPU 正在执行的位置,存的值为正在执行的指令的位置或者下一个将要被执行的指令的位置。

1、挂起当前任务(线程/进程),将这个任务在 CPU 中的状态(上下文)存储于内存中的某处
2、恢复一个任务(线程/进程),在内存中检索下一个任务的上下文并将其在 CPU 的寄存器中恢复
3、跳转到程序计数器所指向的位置(即跳转到任务被中断时的代码行),以恢复该进程在程序中


上下文切换会导致额外的开销,常常表现为高并发执行时速度会慢串行,因此减少上下文切换次数便可以提高多线程程序的运行效率。

  • 直接消耗:指的是CPU寄存器需要保存和加载, 系统调度器的代码需要执行, TLB实例需要重新加载, CPU 的pipeline需要刷掉
  • 间接消耗:指的是多核的cache之间得共享数据, 间接消耗对于程序的影响要看线程工作区操作数据的大小

五、死锁

是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。

1)、死锁的发生必须具备以下四个必要条件。

2)、危害

3)、解决
关键是保证拿锁的顺序一致

两种解决方式

public class NormalDeadLock {
    private static Object No13 = new Object();//第一个锁
    private static Object No14 = new Object();//第二个锁

    //第一个拿锁的方法
    private static void lanceDo() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        synchronized (No14){
            System.out.println(threadName+" get nO14");
            Thread.sleep(100);
            synchronized (No13){
                System.out.println(threadName+" get nO13");
            }
        }
    }

    //第二个拿锁的方法
    private static void avDo() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        synchronized (No13){
            System.out.println(threadName+" get nO13");
            Thread.sleep(100);
            synchronized (No14){
                System.out.println(threadName+" get nO14");
            }
        }
    }

    //子线程
    private static class MyThread extends Thread{
        private String name;
        public MyThread(String name) {
            this.name = name;
        }
        @Override
        public void run() {
            Thread.currentThread().setName(name);
            try {
                lanceDo();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //主线程
        Thread.currentThread().setName("MainThread");
        MyThread myThread = new MyThread("myThread");
        avDo();
        myThread.start();
    }
}
public class TryLock {
    private static Lock No13 = new ReentrantLock();//第一个锁
    private static Lock No14 = new ReentrantLock();//第二个锁

    //先尝试拿No13 锁,再尝试拿No14锁,No14锁没拿到,连同No13 锁一起释放掉
    private static void fisrtToSecond() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        Random r = new Random();
        while(true){
            if(No13.tryLock()){
                System.out.println(threadName +" get 13");
                try{
                    if(No14.tryLock()){
                        try{
                            System.out.println(threadName +" get 14");
                            System.out.println("fisrtToSecond do work------------");
                            break;
                        }finally{
                            No14.unlock();
                        }
                    }
                }finally {
                    No13.unlock();
                }

            }
            Thread.sleep(r.nextInt(3));
        }
    }

    //先尝试拿No14锁,再尝试拿No13锁,No13锁没拿到,连同No14锁一起释放掉
    private static void SecondToFisrt() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        Random r = new Random();
        while(true){
            if(No14.tryLock()){
                System.out.println(threadName +" get 14");
                try{
                    if(No13.tryLock()){
                        try{
                            System.out.println(threadName +" get 13");
                            System.out.println("SecondToFisrt do work------------");
                            break;
                        }finally{
                            No13.unlock();
                        }
                    }
                }finally {
                    No14.unlock();
                }

            }
            Thread.sleep(r.nextInt(3));
        }
    }

    private static class TestThread extends Thread{
        private String name;
        public TestThread(String name) {
            this.name = name;
        }
        public void run(){
            Thread.currentThread().setName(name);
            try {
                SecondToFisrt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Thread.currentThread().setName("TestDeadLock");
        TestThread testThread = new TestThread("SubTestThread");
        testThread.start();
        try {
            fisrtToSecond();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

六、活锁

两个线程在尝试拿锁的机制中,发生多个线程之间互相谦让,不断发生同一个线程总是拿到同一把锁,在尝试拿另一把锁时因为拿不到,而将本来已经持有的锁释放的过程。

解决办法:每个线程休眠随机数,错开拿锁的时间。

上一篇下一篇

猜你喜欢

热点阅读