精华内容
下载资源
问答
  • 进程线程区别,进程线程并发操作是不同
    2018-07-12 16:35:51

    线程和进程各自有什么区别和优劣呢?
        1 进程是资源分配的最小单位,线程是程序执行的最小单位。
        2 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
        3  线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
        但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。

     

    进程的并发只是宏观上的并发,就是不断的上下文切换,不停的切换,其实微观还是来回交替的的

    但是多线程的并发,就是并行加上并发

    缺点:进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。

    进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

    如果这两个缺点理解比较困难的话,举个现实的例子也许你就清楚了:如果把我们上课的过程看成一个进程的话,那么我们要做的是耳朵听老师讲课,手上还要记笔记,脑子还要思考问题,这样才能高效的完成听课的任务。而如果只提供进程这个机制的话,上面这三件事将不能同时执行,同一时间只能做一件事,听的时候就不能记笔记,也不能用脑子思考,这是其一;如果老师在黑板上写演算过程,我们开始记笔记,而老师突然有一步推不下去了,阻塞住了,他在那边思考着,而我们呢,也不能干其他事,即使你想趁此时思考一下刚才没听懂的一个问题都不行,这是其二。

    现在你应该明白了进程的缺陷了,而解决的办法很简单,我们完全可以让听、写、思三个独立的过程,并行起来,这样很明显可以提高听课的效率。而实际的操作系统中,也同样引入了这种类似的机制——线程

    理解进程的并发

    注意:这只是宏观上看来,其实处理器在同一时刻只能处理一个程序,千万不要以为同一时刻处理器在处理多个程(要是有多个处理器那就另当别论了~)。就像你的大脑一样,让你同一时刻算多道运算题,你觉得可能吗。计算机并发地计算多道题目只不过是先计算A题,但是还没执行完,然后又去执行B题,B题还没执行完,下面说不定又去执行A题或者其他的题目。这样在用户看来好像是多个程序在同时执行。当然了这一切的保证是计算机运算速度是相当快的。如果计算机运行的速度非常慢(究竟有多慢自己脑补),你会明显感觉到cpu的控制权在各个程序之间来回交替。

    进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

     

    更多相关内容
  • 本篇文章主要介绍了Python控制多进程与多线程并发数,详细讲诉了进程和线程的区别,并介绍了处理方法,有需要的朋友可以了解一下。
  • // 一个进程可以创建和撤销多个线程 同一个进程中的多个线程之间可以并发执行 //并发 并行 //多线程程序在单核心的cpu上运行 称为并发 //多线程程序在多核心的cpu上运行 称为并行 //并发与并行并不相同 并发主要是由...
  • 一、总论:在JDK提供了几种并发工具类  1)CountDownLatch(同步倒数计数器:等待多线程(或者多步骤)完成)  2)CyclicBarrier(循环屏障:同步屏障)  3)Semaphore(信号量:控制并发进程数)  主要参考...
  • 进程线程和并行并发

    千次阅读 2019-01-17 16:37:40
    定义 进程 进程是具有一定独立功能的程序,关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。一个进程要是能独立运行,它必须拥有一定的资源,包括用于存放程序正文,...线程进程...

    定义

    进程

    进程是具有一定独立功能的程序,关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。一个进程要是能独立运行,它必须拥有一定的资源,包括用于存放程序正文,数据的磁盘和内存地址空间,以及它在运行时所需的I/O设备,已打开的文件,信号量等。
    进程同时又是一个可独立调度和分派的基本单位,一个进程要是能独立运行,它还必须是一个可独立调度和分派的基本单位。

    线程

    线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器),但是它可与同属一个进程的其他线程共享进程所拥有的全部资源

    关系

    在传统的OS中,进程是作为独立调度和分派的基本单位,因而进程是能独立运行的基本单位。在每次被调度的时候,都需要进行上下文切换,开销比较大。而在引入线程的OS中,已把线程作为调度和分派的基本单位,因而线程是能独立运行的基本单位。当线程切换时,仅需保存和设置少量的寄存器内容,切换代价远低于进程。同一个进程中,线程的切换不会引起进程的切换,但从一个进程中的线程切换到另一个进程中的线程时,必然会引起进程的切换.
    一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行;
    相对进程而言,线程是一个更加接近执行体的概念,它可以与同进程中的其他线程共享数据,因此线程可以读出同样的数据结构和变量,便于线程之间的通信,但拥有自己的栈空间,拥有独立的执行序列

    区别

    主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程产生影响,而线程只是进程中的不同执行路径。进程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于进程死掉,所以多进程的程序要比多线程的程序更健壮,但在进程切换时,耗费资源较大,但对于一些要求同时进行并且又要共享某些变量的并发操作,只能使用线程,不能使用进程

    1. 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

    2. 线程的划分尺度小于进程,使得多线程程序的并发性高。

    3. 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

    4. 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

    5. 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

    优缺点

    线程执行开销小,但不利于资源的管理和保护。而进程相反,同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移

    多线程应用

    例如并发读取两个文件,文件读取完毕后cpu才开始处理,完成文件读取前cpu一直空闲。如果调整操作顺序,cpu的利用率会提高,程序执行时间也会缩短,在读取B文件时候cpu可以处理文件A,通常情况,cpu在等待IO时,可以执行命令。IO可以是文件IO、网络IO、输入输出IO等。这种情况经常会发生,因为IO速度比cpu的处理速度慢很多。(前提保证,执行顺序不会影响最后结果,结果大于性能,例如一次付款,执行顺序会影响结果就不适合多线程并行执行),多线程是为了更充分的利用cpu资源,再例如服务器在一个端口监听用户请求,服务器处理完用户的请求后继续监听其他的用请求,这样带来的问题是,服务器在处理用户的请求过程中,其他的请求是无法监听和接收的。为解决这个问题,可以采用多线程的方式,每个用户请求的处理都交给一个线程去做。这样,服务器在处理用户请求时可以继续监听端口上其他用户请求

    并发与并行

    并发是指同一时间片段同时执行,并行是指同一时间点同时执行。进程之间相互独立可以实现并行,线程不可以。多线程只能并发执行,而且是顺序执行,只是在同一时间片段,类似同时执行,CPU可以按时间切片执行,单核CPU同一时刻只支持一个线程执行任务,多线程并发事实上就是多个线程排队申请调用CPU,CPU执行速度很多,所以看上去是多个线程任务说是并发处理。
    展开全文
  • 线程并发则由程序员管理并发处理的任务,这种并发方式可以方便地在线程间共享数据(前提是不能互斥)。Python对多线程和多进程的支持都比一般编程语言更高级,最小化了需要我们完成的工作。 一.多进程并发 Mark ...
  • 主要介绍了Java多线程并发执行demo代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • tcp socket实现单进程线程并发服务端源码 c++源码
  • 主要介绍了php多线程并发实现方法,结合实例形式分析了php模拟多线程并发的相关操作技巧,以及在Linux平台借助shell实现多线程并发的操作方法,需要的朋友可以参考下
  • 压缩包内包含四个文件,实现的效果都是通过多线程或多进程执行加法运算; multiprocess_queue,通过任务队列方式实现多进程任务;(multiprocessing模块) multithread_queue,通过任务队列方式实现多线程任务;...
  • 操作系统上机实验报告 实验名称 进程线程 实验目的 理解uix/Linux下进程线程的创建并发执行过程 实验内容 1进程的创建 2多线程应用 实验步骤及分析 进程的创建 下面这个C程序展示了UNIX系统进程创建子进程及...
  • 对于一个进程中的多个线程来说,多个线程共享进程的内存块,当有新的线程产生的时候,操作系统不分配新的内存,而是让新线程共享原有的进程块的内存。因此,线程间的通信很容易,速度也很快。不同进程因为处于不同...
  • 在Python实现web服务器入门学习笔记(3)——多进程、多线程实现并发HTTP服务器,我们知道可以分别通过多进程、多线程的方式实现并发服务器,那么,是否可以通过单进程线程的程序实现类似功能呢? 实际上,在...
  • 线程并发下载图片4.多进程并发提高数字运算在计算机编程领域,并发编程是一个很常见的名词和功能了,其实并发这个理念,最初是源于铁路和电报的早期工作。比如在同一个铁路系统上如何安排多列火车,保证每列火车的...
  • 3.多线程并发下载图片 4.多进程并发提高数字运算 关于并发 在计算机编程领域,并发编程是一个很常见的名词和功能了,其实并发这个理念,最初是源于铁路和电报的早期工作。比如在同一个铁路系统上如何安排多列火车,...
  • 即使是单CPU的计算机,也可以通过不停地在不同线程的指令间切换,从而造成多线程同时运行的效果。 多线程相当于一个并发(concunrrency)系统。并发系统一般同时执行多个任务。如果多个任务可以共享资源,特别是同时...
  • 线程并发

    千次阅读 2019-04-14 17:04:34
    文章目录1、什么是进程2、进程的状态3、线程4、线程的基本使用5、线程休眠6、守护线程与yield7、join和中断线程8、线程同步同步代码块:同步方法Lockvolatile阻塞队列使用原子变量实现线程同步9、死锁经典案例面试题...

    1、什么是进程

    程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念,而进程是程序在处理机上的一次执行过程,是个动态的概念。

    进程是一个具有一定独立功能的程序,一个实体,每一个进程都有自己的地址空间。

    2、进程的状态

    进程执行的间断性决定了进程可能具有多种状态,事实上,运行中的进程具有以下三种状态:

    • 就绪状态
    • 运行状态
    • 阻塞状态

    3、线程

    线程实际上是在进程的基础上进一步划分,一个进程启动后。里面的若干程序又可以划分若干个线程。

    线程:是进程的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程

    并行:两个任务同时运行(多个CPU)

    并发:就是将任务轮流执行,由于CPU时间片运行时间较短,就会感觉两个任务在同时执行。

    4、线程的基本使用

    • 一种是继承Thread类
    class MyThread extends Thread{
        public void run{
            //逻辑处理
        }
    }
    MyThread mt = new MyThread();
    mt.start()
    
    • 另外一种是实现Runnable接口(推荐使用)
    class MyRunnable implements Runnable{
        public void run(){
            //逻辑处理
        }
    }
    MyRunnable mr = new MyRunnable();
    Thread t = new Thread(mr);
    t.start();
    

    5、线程休眠

    pulic static void sleep(long mills)
    
    在当前线程的执行中,暂停指定的毫秒数,释放CPU的时间片
    

    6、守护线程与yield

    public final void setDaemon(boolean on)
    将此线程标记为daemon线程或用户线程,当运行的唯一线程都是守护进程线程时,Java虚拟机将退出
    参数为true时,线程为守护线程
    
    public static void yield()
    暂停当前正在执行的线程对象,并执行其他线程
    
    void setPriority(int newPriority) 
    更改线程优先级
    static int MAX_PRIORITY 最大优先级,抢到CPU时间片的概率大
    static int MIN_PRIORITY 最小优先级
    static int NORM_PRIORITY 默认优先级
    
    package per.learn.multhread;
    
    
    public class ThreadDemo2 {
        public static void main(String[] args) {
            MyRunnable2 mr = new MyRunnable2();
            Thread tr  = new Thread(mr);
            tr.setDaemon(true);//设置为守护线程
            tr.start();
            for(int i =0;i<10;i++){
                System.out.println("main--"+i);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    class MyRunnable2 implements Runnable{
        private long millis;
    
        @Override
        public void run() {
            for(int i =0;i<10;i++){
                System.out.println("--"+i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    OUT:
    main--0
    --0
    main--1
    main--2
    --1
    main--3
    main--4
    --2
    main--5
    main--6
    main--7
    --3
    main--8
    main--9
    --4
    
    main运行完,线程tr并未执行完。
    

    7、join和中断线程

    public final void join() throws InterruptedException
    等待这个线程死亡
    调用此方法的行为方式与调用相同
    join(0)
    
    public void interrupt()
    中断这个线程
    除非当前线程中断自身,这是允许的
    public static boolean interrupted()
    测试当前线程是否中断,该方法会清除中断状态
    

    join方法:
    加入线程,将调用的线程执行指定时间或执行完毕,再执行其他

    package per.learn.multhread;
    
    public class ThreadDemo3 {
        public static void main(String[] args) {
            MyRunnable3 mr3 = new MyRunnable3();
            Thread t = new Thread(mr3);
            t.start();
            for(int i =0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+"--"+i);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(i==5){
                    try{
                        t.join();//让线程t执行完毕
                    }catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //t.interrupt();//中断线程,只是做了中断标记,抛出异常,线程仍然会执行
                }
            }
        }
    }
    class MyRunnable3 implements Runnable{
        @Override
        public void run() {
            for(int i =0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+"--"+i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    Out:
    main--0
    Thread-0--0
    main--1
    main--2
    Thread-0--1
    main--3
    main--4
    Thread-0--2
    main--5
    Thread-0--3
    Thread-0--4
    Thread-0--5
    Thread-0--6
    Thread-0--7
    Thread-0--8
    Thread-0--9
    main--6
    main--7
    main--8
    main--9
    
    
    package per.learn.multhread;
    
    public class ThreadDemo3 {
        public static void main(String[] args) {
            MyRunnable3 mr3 = new MyRunnable3();
            Thread t = new Thread(mr3);
            t.start();
            for(int i =0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+"--"+i);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(i==5){
    //                try{
    //                    t.join();//让线程t执行完毕
    //                }catch (InterruptedException e) {
    //                    e.printStackTrace();
    //                }
                    t.interrupt();//中断线程,只是做了中断标记,抛出异常,线程仍然会执行
                }
            }
        }
    }
    class MyRunnable3 implements Runnable{
        @Override
        public void run() {
            for(int i =0;i<10;i++){
                if(Thread.interrupted()){
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"--"+i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace(); //会将中断状态清除
                    Thread.currentThread().interrupt();//将中断状态重新标记
                }
            }
        }
    }
    
    
    main--0
    Thread-0--0
    main--1
    main--2
    Thread-0--1
    main--3
    main--4
    main--5
    Thread-0--2
    main--6
    java.lang.InterruptedException: sleep interrupted
    	at java.lang.Thread.sleep(Native Method)
    	at per.learn.multhread.MyRunnable3.run(ThreadDemo3.java:35)
    	at java.lang.Thread.run(Thread.java:748)
    main--7
    main--8
    main--9
    

    8、线程同步

    • 多线程共享数据
      在多线程的操作中,多个线程可能同时处理同一个资源,这就是多线程中的共享数据,会发生不安全的情况。
    • 线程同步

    同步代码块:

    同步是一种高开销的操作,因此应该尽量减少同步的内容。
    通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

    synchronized(object){ 
        要同步的操作
    }
    

    例:

    package per.learn.multhread;
    
    import sun.awt.windows.ThemeReader;
    
    public class ThreadDemo4 {
        public static void main(String[] args) {
            MyRunnable5 mr5 = new MyRunnable5();
            Thread t1 = new Thread(mr5);
            Thread t2 = new Thread(mr5);
            //模拟两个窗口
            t1.start();
            t2.start();
        }
    }
    class MyRunnable5 implements Runnable{
        private int ticket=10;//车票
        private Object obj =  new Object();
        @Override
        public void run() {
            for(int i=0;i<300;i++){
                synchronized (obj){
                    if(ticket > 0){
                        ticket--;
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("您购买的车票已剩余"+ticket+"张");
                    }
                }
            }
        }
    }
    

    同步方法

    public synchronized void method(){
    要同步的操作
    }

    同步的方法是当前对象(this)

    注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类

    package per.learn.multhread;
    
    import sun.awt.windows.ThemeReader;
    
    public class ThreadDemo4 {
        public static void main(String[] args) {
            MyRunnable5 mr5 = new MyRunnable5();
            Thread t1 = new Thread(mr5);
            Thread t2 = new Thread(mr5);
            //模拟两个窗口
            t1.start();
            t2.start();
        }
    }
    class MyRunnable5 implements Runnable{
        private int ticket=10;//车票
        private Object obj =  new Object();
        private synchronized void method(){
            if(ticket > 0){
                ticket--;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("您购买的车票已剩余"+ticket+"张");
            }
        }
        @Override
        public void run() {
            for(int i=0;i<300;i++){
                method();
                    }
                }
    }
    

    Lock

    结构化更加方便,以上两种只能等同步方法或同步代码块执行完毕后才能释放锁。
    ReentrantLock类是可重入、互斥、实现了Lock接口的锁,
    它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力

    ReenreantLock类的常用方法有:
    
        ReentrantLock() : 创建一个ReentrantLock实例 
        lock() : 获得锁 
        unlock() : 释放锁 
    注:ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用 
    
    package per.learn.multhread;
    
    import sun.awt.windows.ThemeReader;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ThreadDemo4 {
        public static void main(String[] args) {
            MyRunnable5 mr5 = new MyRunnable5();
            Thread t1 = new Thread(mr5);
            Thread t2 = new Thread(mr5);
            //模拟两个窗口
            t1.start();
            t2.start();
        }
    }
    class MyRunnable5 implements Runnable{
        private int ticket=10;//车票
        private Object obj =  new Object();
        ReentrantLock lock = new ReentrantLock();
        private void method2(){
            lock.lock();//锁
            try{
                if(ticket > 0){
                    ticket--;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("您购买的车票已剩余"+ticket+"张");
                }
            }finally {
                lock.unlock();//释放锁
            }
        }
        @Override
        public void run() {
            for(int i=0;i<300;i++){
                method2();
                    }
                }
    }
    

    注意:

    • 如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码
    • 如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁

    volatile

    • volatile关键字为域变量的访问提供了一种免锁机制,
    • 使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
    • 因此每次使用该域就要重新计算,而不是使用寄存器中的值
    • volatile不会提供任何原子操作,它也不能用来修饰final类型的变量

    阻塞队列

    LinkedBlockingQueue 类常用方法

    • LinkedBlockingQueue() : 创建一个容量为Integer.MAX_VALUE的LinkedBlockingQueue
    • put(E e) : 在队尾添加一个元素,如果队列满则阻塞
    • size() : 返回队列中的元素个数
    • take() : 移除并返回队头元素,如果队列空则阻塞

    当队列满时:
    add()方法会抛出异常
    offer()方法返回false
    put()方法会阻塞

    使用原子变量实现线程同步

    需要使用线程同步的根本原因在于对普通变量的操作不是原子的。

    那么什么是原子操作呢?
    原子操作就是指将读取变量值、修改变量值、保存变量值看成一个整体来操作
    即-这几种行为要么同时完成,要么都不完成。

    在java的util.concurrent.atomic包中提供了创建了原子类型变量的工具类,
    使用该类可以简化线程同步。

    其中AtomicInteger 表可以用原子方式更新int的值,可用在应用程序中(如以原子方式增加的计数器),
    但不能用于替换Integer;可扩展Number,允许那些处理机遇数字类的工具和实用工具进行统一访问。

    AtomicInteger类常用方法:
    AtomicInteger(int initialValue) : 创建具有给定初始值的新的AtomicInteger
    addAddGet(int dalta) : 以原子方式将给定值与当前值相加
    get() : 获取当前值

    9、死锁

    过多的同步有可能出现死锁。

    经典案例

    如:服务员需要等待厨师准备好食物,然后通知服务员上菜,然后回去继续等待,厨师代表生产者,服务员代表消费者。

    package per.learn.multhread;
    
    /**
     * 两个线程协同工作,先生产,再消费
     */
    public class ProducterCustomerDemo {
        public static void main(String[] args) {
            Food food = new Food();//共享food对象
            Producter p = new Producter(food);
            Customer c = new Customer(food);
            //先生产,再消费
            Thread t1 = new Thread(p);
            Thread t2 = new Thread(c);
            t1.start();
            t2.start();
        }
    }
    /**
     * 消费者
     */
    class Customer implements Runnable{
        private Food food;
        public Customer(Food food){
            this.food =food;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                food.get();
            }
        }
    }
    
    /**
     * 生产者
     */
    class Producter implements Runnable{
        private Food food;
        public Producter(Food food){
            this.food=food;
        }
    
        @Override
        public void run() {
            //模拟生产20份
            for (int i = 0; i <20 ; i++) {
                if(i%3==0){
                    food.set("锅包肉","酸甜口味");
                }
                else if(i%3==1){
                    food.set("佛跳墙","多辣");
                }else{
                    food.set("小炒肉","多肉");
                }
            }
        }
    }
    /*
    共享
     */
    class Food{
        private String name;
        private String describe;
        private boolean flag = true;//true 表示生产,false表示消费
        /**
         * 生产产品
         */
        public synchronized void set(String name,String describe){
            if(!flag){
                //表示不能生产,要释放cpu和锁
                try {
                    this.wait();//线程进入等待状态,释放监视器所有权(对象锁)
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.setName(name);
            try {
                Thread.sleep(1000);  //模拟生产过程 产生时间差
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.setDescribe(describe);
            flag = false;
            this.notify();//唤醒等待的线程(随机的其中一个)
        }
    
        /**
         * 消费
         */
        public synchronized  void get(){
            if(flag){
                //表示不能消费
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this.getName()+"->"+this.getDescribe());
            flag = true;
            this.notify();
        }
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDescribe() {
            return describe;
        }
    
        public void setDescribe(String describe) {
            this.describe = describe;
        }
    
        @Override
        public String toString() {
            return "Food{" +
                    "name='" + name + '\'' +
                    ", describe='" + describe + '\'' +
                    '}';
        }
    
        public Food(String name, String describe) {
            this.name = name;
            this.describe = describe;
        }
    
        public Food() {
        }
    }
    

    线程生命周期和线程池

    在这里插入图片描述
    线程池是预先创建线程的一种技术。线程池在还没有任务来之前,创建一定数量的线程,放入空闲队列中,然后对这些资源进行复用。减少频繁的创建和销毁对象。线程池接口是ExecutorService。

    Executor接口:
    执行已提交的Runnable任务对象

    ExecutorService:
    Executor提供了管理终止方法,以及可为追踪一个或多个异步任务执行状况而生成Future的方法。

    Executors类:
    此包中所定义的Executor、ExecutorService等的工厂和实用方法。

    newSingleThreadExecutor:
    创建单线程线程池。相当于单线程串行执行所有任务,如果这个唯一线程因为异常结束,那么会有一个新的线程替代它,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

    newFixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到达到线程池最大大小。
    如果某个线程因为异常结束,就会补充一个新线程

    newCachedThredPool: 创建一个可缓存的线程池,如果线程池的大小超过了处理任务所需要的线程,那么就会收回部分空闲(60s不执行任务)的线程,线程池不会对线程池大小做限制,依赖操作系统(或者说JVM)能创建的最大线程池大小。

    newSchedeledThreadPool:创建一个大小无限的线程池,支持定时及周期性执行任务的需求。(需要指定初始大小)

    package per.learn.multhread;
    import java.util.concurrent.*;
    
    public class ThreadDemo5 {
        public static void main(String[] args) {
            //创建线程池(4种)
            //ExecutorService es = Executors.newSingleThreadExecutor();
            //ExecutorService es = Executors.newFixedThreadPool(2);
            ScheduledExecutorService es = Executors.newScheduledThreadPool(2);
            //es.execute(new MyRunnable6());
            //es.execute(new MyRunnable6());
            es.schedule(new MyRunnable6(),3000, TimeUnit.MILLISECONDS);
    
            es.shutdown();//结束
        }
    }
    class MyRunnable6 implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+"---"+i);
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    面试题

    sleep() wait de 区别:
    sleep:让线程进入休眠状态,让出CPU时间时间片,不释放对象锁

    wait:让线程进入等待状态,让出CPU时间片,释放对象锁,等待其他线程通过notify()方法来唤醒,也可以设置等待时间,该线程再次竞争资源

    展开全文
  • 一个进程中可以并发多条线程,每条线程并行执行不同的任务。 Num02–>进程 进程就是一个程序在一个数据集上的一次动态执行过程进程有以下三部分组成: 1,程序:我们编写的程序用来描述进程要完成哪些功能以及...
  • 线程并发执行)

    千次阅读 2020-08-02 15:06:54
    ​ 当系统有一个以上CPU时,同一时刻,当一个CPU在执行一个任务时,另一个CPU在执行另一个任务,两个任务互不抢占CPU资源,可以同时进行(多核CPU,一个CPU执行一个进程并发 ​ 一个CPU,同一时间,有多个任务在...

    一、概念区分

    1、并行与并发

    并行

    ​ 当系统有一个以上CPU时,同一时刻,当一个CPU在执行一个任务时,另一个CPU在执行另一个任务,两个任务互不抢占CPU资源,可以同时进行(多核CPU,一个CPU执行一个进程)

    并发

    ​ 一个CPU,同一时间,有多个任务在执行。但并发不是真正意义上的“同时进行”,只是将CPU划分成好几个时间片段,每个片段内执行一个任务,然后在这几个片段之间来回切换,由于CPU处理速度快,让用户感觉像是多个任务在同时执行。

    区别:

    • 并行是某一时刻,真正有多个程序在运行;并发是在一段时间内,宏观上多个程序同时运行。

    • 并发,指多个事情,在同一时间段内同时发生了;多个任务之间是相互抢占资源的

      并行,指多个事情,在同一时间点上同时发生了;多个任务之间是不相互抢占资源的

    • 只有在多个CPU或CPU多核时,才会发生并行,否则看似同时发生的事情,都是并发的

    2、进程与线程

    进程

    ​ 指系统中正在运行的一个应用程序;是资源分配的最小单位

    线程

    ​ 是进程内独立执行的一个单一顺序的控制流;是系统分配处理器时间资源的基本单位;是程序执行的最小单位

    在这里插入图片描述

    区别

    • 进程之间数据不共享
    • 线程之间可以共享资源

    二、线程的生命周期

    ​ 生命周期:在程序开发中,一个对象从被实例化完成,到这个对象使用结束并销毁的整个过程,类似于人的一生

    线程的生命周期:一个线程被实例化,到这个线程销毁的整个过程

    线程的状态

    • 新建:New

    ​ 一个线程被实例化完成,但是还没有做任何动作

    • 就绪:Ready

    ​ 一个线程已经被启动 (调用start()方法),开始争抢CPU的时间片

    • 运行:Run

    ​ 一个线程抢到了CPU的时间片,开始执行这个线程中的逻辑

    • 阻塞:Interrupt

    ​ 一个线程在运行的过程中,受到某些操作的影响,放弃已经获取的CPU时间片,并且不再参与CPU时间片的争抢,此时线程处于挂起状态

    • 死亡:Dead

    ​ 一个线程对象需要被销毁
    在这里插入图片描述

    三、开启线程的方式

    1、继承Thread类,实现其run()方法

    //要自定义一个线程类,并且该类要继承Thread类
    class MyThread extends Thread{
    	//重写run方法
    	@Override
    	public void run() {
    		for(int i=0;i<5;i++) {
    			System.out.println("子线程逻辑:"+i);
    		}
    	}
    }
    public class ThreadClass {
    	public static void main(String[] args) {
    		MyThread mt=new MyThread();  //新建
    		mt.start();    //就绪
    		System.out.println("主线程逻辑执行结束");
    	}
    }
    /*输出结果:
    主线程逻辑执行结束
    子线程逻辑:0
    子线程逻辑:1
    子线程逻辑:2
    子线程逻辑:3
    子线程逻辑:4
    */
    

    如果是串行运行,则“主线程逻辑执行结束”这句话应该最后执行。但由于并发执行的多线程存在,使得主程序逻辑先执行完毕,在执行子线程

    注意:只有调用start方法才会启动线程,并且使该线程执行run方法;如果直接调用run方法,则并没有开启线程,即线程不会进入就绪状态。

    2、实现Runnable接口,实现其run()方法

    /*
     * Runnable接口是一个函数式接口,可以采用Lambda表达式实现其run方法
     */
    public class ThreadClass {
    	public static void main(String[] args) {
    		Runnable r1=()->{
    			for(int i=0;i<5;i++) {
    				System.out.println("子线程中的逻辑:"+i);
    			}
    		};
    		Thread t=new Thread(r1);  //新建
    		t.start();    //就绪
    		System.out.println("主线程逻辑执行结束");
    	}
    }
    //输出结果同上
    

    3、实现Callable接口

    与Runnable接口类似,只是该方式有返回值,但Runnable没有返回值

    需要使用一个中介FutureTask

    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class Test {
    	public static void main(String[] args) {
    		//返回值是int类型
    		Callable callable=()->{
    			int result=0;
    			for(int i=0;i<100;i++) {
    				result+=i;
    			}
    			return result;
    		};
    		//Thread thread=new Thread(callable); 不能直接像创建Runnable接口一样
    		//知道返回值是int性。使用泛型约束
    		FutureTask<Integer> task=new FutureTask<> (callable);
    		
    		Thread thread=new Thread(task);
    		thread.start();
    		
    		//获取计算结果
    		Integer integer = null;
    		try {
    			integer = task.get();  //该方法会抛出两个异常,需要手动处理
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		} catch (ExecutionException e) {
    			e.printStackTrace();
    		}  
    		System.out.println(integer);
    	}
    }
    

    4、异同点

    • 继承Thread类,可读性更高,但是如果某个类类继承了Thread类,那么该类将不能再继承其他类,这有可能会破坏原有的继承结构
    • 使用Runnable接口,程序可对象降低,但不会破坏继承结构,一般多使用这种方式

    四、线程的常用方法

    1、线程的命名setName

    • 实例化一个线程,使用setName()方法
    • 实例化一个线程的同时,通过构造方法对线程进行命名
    • 3、使用用户自定义的线程类,在实例化的同时,进行名字的赋值
      需要给自定义线程类添加对应的构造方法
    class MyThread extends Thread{
    	public MyThread() {}
    	public MyThread(String name) {
    		this.setName(name);     //使用setName()方法
    		//super(name);  //直接调用父类的构造方法
    	}
    }
    public class ThreadClass {
    	public static void main(String[] args) {
    		//1、实例化一个线程,使用setName()方法
    		Thread t=new Thread();
    		t.setName("用户线程1");
    		System.out.println(t.getName());
    
    		//2、实例化一个线程的同时,通过构造方法对线程进行命名
    		//  构造方法:Thread(Runnable r,String name);
    		Thread t2=new Thread(()->{},"用户线程2"); 
    		System.out.println(t2.getName());
    		
    		//3、使用用户自定义的线程类,在实例化的同时,进行名字的赋值
    		//   需要给自定义线程类添加对应的构造方法
    		MyThread t3=new MyThread("用户线程3");
    		System.out.println(t3.getName());
    	}
    }
    

    2、线程休眠sleep(Run->Interrupt)

    1. 调用**sleep()**方法,参数:以毫秒为单位的时间差
    2. 会抛出InterruptedException异常,需要处理
    3. 使得线程由运行状态变为阻塞状态,当休眠时间到达时,才会重新变为就绪状态。即使此时系统中没有其他可执行的线程,处于sleep的线程也依然不会执行
    class MyThread extends Thread{
    	//重写run方法
    	@Override
    	public void run() {
    		for(int i=0;i<5;i++) {
    			System.out.println(+i);
    			//线程休眠
    			//参数:以毫秒为单位
    			//需要捕获异常
    			try {
    				Thread.sleep(1000);  //休眠1秒
    			} 
    			catch (InterruptedException e) {
    				e.printStackTrace();
    			}   
    		}
    	}
    }
    public class ThreadClass {
    	public static void main(String[] args) {
    		//调用threadSleep方法
    		threadSleep();
    	}
        /****线程休眠****/
    	public static void threadSleep() {
    		//实例化一个线程
    		MyThread mt=new MyThread();
    		mt.start();
    	}
    }
    //输出形式:每隔1秒输出一个i值
    

    3、线程的优先级setPriority

    1. 调用**setPriority()**方法,参数:[0,10]范围内的一个整数,默认是5
    2. 设置优先级,只是设置这个线程可以抢到CPU时间片的概率,并不是优先级高的线程一定能抢到CPU时间片(不是优先级高的线程一定先执行,也不是优先级高的线程执行完再执行其他线程)
    3. 设置优先级必须要放在线程开始(start)之前
    public class ThreadClass {
    	public static void main(String[] args) {
    		threadPriority();
    	}
        /****设置线程的优先级***/
    	public static void threadPriority() {
    		Runnable r=()->{
    			for(int i=0;i<5;i++){
    					System.out.println(Thread.currentThread().getName()+":"+i);
    			}
    		};
            //1、线程实例化
    		Thread t1=new Thread(r,"Thread-1");
    		Thread t2=new Thread(r,"Thread-2");
    		
    		//2、设置优先级, 必须要将该操作放在线程开始(start)之前
    		t1.setPriority(10);
    		t2.setPriority(1);
    		
            //3、线程启动
    		t1.start();
    		t2.start();
    	}
    }
    //输出结果:交替执行
    

    4、线程的礼让yield(Run->Ready)

    1. 调用**yield()**方法,类方法
    2. 线程礼让是指让当前运行的线程释放自己的CPU资源,由运行状态,回到就绪状态。**但并不意味着一定去执行另一个线程,**此时依然是两个线程进行CPU时间片的抢夺
    public class ThreadClass {
    	public static void main(String[] args) {
    		threadYield();
    	}
    	/***线程的礼让***/
    	public static void threadYield() {
    		Runnable r=()->{
    			for(int i=0;i<10;i++) {
    				System.out.println(Thread.currentThread().getName()+":"+i);
    				//线程礼让
    				if(i==3) {
    					Thread.yield();
    				}
    			}
    		};
    		Thread t1=new Thread(r,"Thread-1");
    		Thread t2=new Thread(r,"Thread-2");
    		
    		t1.start();
    		t2.start();
    	}
    }
    /*输出结果:
    Thread-2:0
    Thread-2:1
    Thread-2:2
    Thread-2:3   //Thread-2礼让,CPU被Thread-1抢到
    Thread-1:0
    Thread-1:1
    Thread-1:2
    Thread-1:3  //Thread-1礼让,但是CPU还是被Thread-1抢到,Thread-1继续执行
    Thread-1:4
    Thread-1:5
    Thread-1:6
    Thread-1:7
    Thread-1:8
    Thread-1:9   //Thread-1执行完毕,Thread-2接着执行
    Thread-2:4
    Thread-2:5
    Thread-2:6
    Thread-2:7
    Thread-2:8
    Thread-2:9
    */
    

    5、线程合并join

    1. 执行join的线程,在该过程中,其他线程阻塞,待此线程执行完毕,再执行其他线程。(插队)
    2. 抛出InterruptException异常
    public class JoinTest {
    
    	public static void main(String[] args) {
    		Runnable runnable=()->{
    			for(int i=0;i<100;i++) {
    				System.out.println("vip线程"+i);
    			}
    		};
    		
    		Thread thread=new Thread(runnable);
    		thread.start();
    		//主线程输出100次
    		for(int i=0;i<100;i++) {
    			/*
    			 * 当主线程运行到第50次时,调用join方法,那么此时会等join方法加入的线程执行完毕,在执行主线程
    			 * */
    			if(i==50) {
    				try {
    					thread.join();
    				} 
    				catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    			System.out.println("main"+i);
    		}
    	}
    }
    /*输出:在50之前,主线程和子线程交替执行,但是等到主线程为50时,此时子线程会执行直到100结束,然后主线程才执行
    */
    

    6、守护线程setDaemon

    1. 如果所有的用户线程结束,那么守护线程会自动死亡;虚拟机不需要等待守护线程执行结束
    2. setDaemon默认是false,如果要设置一个线程为守护线程,则改为true即可
    public class DaemonTest {
    	public static void main(String[] args) {
    		Runnable r1=()->{
    			while(true) {
    				System.out.println("守护线程");
    			}
    		};
    		
    		for(int i=0;i<10;i++) {
    			System.out.println("主线程"+i);
    		}
    		
    		Thread thread=new Thread(r1);
    		thread.setDaemon(true);  //默认是false,表示用户线程
    		thread.start();
    	}
    }
    //守护线程是一个死循环,但是等待主线程执行结束后,该线程会自动停止
    

    五、线程安全问题

    临界资源:多个线程共享的资源。当多个线程同时去访问这个共享资源时,会出现线程安全问题

    1、产生的原因

    当一个线程在访问并操作某个资源的过程中,还没来得及完全修改该资源,CPU时间片就被其他线程抢走

    //用四个线程模拟四个售票员卖票,仓库中的余票即为临界资源
    class TicketCenter{
    	//描述剩余票的数量
    	public static int restCount=100;
    }
    public class SourseProblem {
    	public static void main(String[] args) {
    		Runnable r=()->{
    			//当余票大于0时,可以继续售票
    			while(TicketCenter.restCount>0) {
    				System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+ --TicketCenter.restCount+"张");
    			}
    		};
    		//四个线程模拟四个售票员,线程名模拟售票员名
    		Thread t1=new Thread(r,"Thread-1");
    		Thread t2=new Thread(r,"Thread-2");
    		Thread t3=new Thread(r,"Thread-3");
    		Thread t4=new Thread(r,"Thread-4");
    		
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    }
    

    输出结果:

    在这里插入图片描述

    出现临界资源问题,这是因为一个线程在计算余票的过程中,还没来的及将计算、或计算后的结果还没来得及赋给restCount,CPU就被其他线程抢走,此时其他线程中的余票是当前抢到时刻的余票值。

    2、解决方法

    • JVM实现的synchronized
    • JDK实现的ReentrantLock

    方式一:使用同步代码块

    用synchronized修饰多线程需要访问的代码

    class TicketCenter{
    	//描述剩余票的数量
    	public static int restCount=100;
    }
    public class SourseProblem {
    	public static void main(String[] args) {
    		Runnable r=()->{
    			//当余票大于0时,可以继续售票
    			while(TicketCenter.restCount>0) {
    				//同步监视器
    				synchronized("") {
    					if(TicketCenter.restCount<=0) {
    						return;
    					}
    					System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+ --TicketCenter.restCount+"张");
    				}
    			}
    		};
    		//四个线程模拟四个售票员,线程名模拟售票员名
    		Thread t1=new Thread(r,"Thread-1");
    		Thread t2=new Thread(r,"Thread-2");
    		Thread t3=new Thread(r,"Thread-3");
    		Thread t4=new Thread(r,"Thread-4");
    		
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    }
    

    方法二:同步方法:使用关键字synchronized修饰的方法

    将上面的同步代码段用一个方法实现

    1. 静态方法:同步监视器就是:当前类.class
    2. 非静态方法:同步监视器是 this
    class TicketCenter{
    	//描述剩余票的数量
    	public static int restCount=100;
    }
    public class SourseProblem {
    	public static void main(String[] args) {
    		Runnable r=()->{
    			while(TicketCenter.restCount>0) {
    				soldTicket();
    			}
    		};
    		Thread t1=new Thread(r,"Thread-1");
    		Thread t2=new Thread(r,"Thread-2");
    		Thread t3=new Thread(r,"Thread-3");
    		Thread t4=new Thread(r,"Thread-4");
    	
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    	//同步方法
    	public synchronized static void soldTicket(){
    		if(TicketCenter.restCount<=0) {
    			return;
    		}
    		System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+ --TicketCenter.restCount+"张");
    	}
    }
    

    方式三:同步锁

    显式定义同步锁对象来实现同步

    class TicketCenter{
    	//描述剩余票的数量
    	public static int restCount=100;
    }
    public class SourseProblem {
    	public static void main(String[] args) {
    		//实例化一个锁对象
    		ReentrantLock rt=new ReentrantLock();
    		
    		Runnable r=()->{
    			while(TicketCenter.restCount>0) {
    				//对临界资源上锁
    				rt.lock();
    				
    				if(TicketCenter.restCount<=0) {
    					return;
    				}
    				System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+ --TicketCenter.restCount+"张");
    				
    				//对临界资源解锁
    				rt.unlock();
    			}
    		};
    		Thread t1=new Thread(r,"Thread-1");
    		Thread t2=new Thread(r,"Thread-2");
    		Thread t3=new Thread(r,"Thread-3");
    		Thread t4=new Thread(r,"Thread-4");
    		
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    }
    

    3、死锁

    多个线程彼此持有对方所需要的锁,而不释放自己的锁

    //线程A、B互相等待对方释放拥有的锁
    public class DeadLock {
    	public static void main(String[] args) {
            
    		Runnable runnable1=()->{
    			synchronized("A"){
    				System.out.println("A线程持有了A锁,等待B锁");
    				//此时A线程已经持有A锁了,让它继续持有B锁
                    /*为了确保产生死锁
                    try {
    					Thread.sleep(1000);
    				} 
    				catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}*/
                    
    				synchronized("B"){
    					System.out.println("A线程持有了A锁和B锁");
    				}
    			}
    		};
    		
    		Runnable runnable2=()->{
    			synchronized("B"){
    				System.out.println("B线程持有了B锁,等待A锁");
    				//此时B线程已经持有B锁了,让它继续去持有A锁
    				synchronized("A"){
    					System.out.println("B线程持有了A锁和B锁");
    				}
    			}
    		};
    		
    		Thread t1=new Thread(runnable1);
    		Thread t2=new Thread(runnable2);
    		
    		t1.start();
    		t2.start();
    	}
    }
    /*输出结果:
    B线程持有了B锁,等待A锁
    A线程持有了A锁,等待B锁
    (程序未结束)
    */
    

    上述代码其实不能完全产生死锁,如果在A线程获取B锁之前,B线程都没有获得执行机会,那么B线程就不会获取到B锁,此时程序依然会执行,不会产生死锁。为了一定产生死锁情况,可以在A线程执行过程中调用一个sleep方法。

    4、线程通信:解决死锁的办法

    方式1:synchronized下的通信

    • wait():等待,当前的线程释放对同步监视器的锁定,并且让出CPU资源,使得当前的线程进入等待队列中
    • notify():通知,唤醒在此同步监视器上等待的一个线程(具体哪一个由CPU决定),使这个线程进入锁池
    • notifyAll():通知,唤醒在此同步监视器上等待的所有线程,使这些线程进入锁池
    public class DeadLock {
    	public static void main(String[] args) {
            
    		Runnable runnable1=()->{
    			synchronized("A"){
    				System.out.println("A线程持有了A锁,等待B锁");
    				//A线程释放A锁(捕获异常)
    				try {
    					"A".wait();
    				} 
    				catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    				
    				synchronized("B"){
    					System.out.println("A线程持有了A锁和B锁");
    				}
    			}
    		};
    		
    		Runnable runnable2=()->{
    			synchronized("B"){
    				System.out.println("B线程持有了B锁,等待A锁");
    				
    				synchronized("A"){
    					System.out.println("B线程持有了A锁和B锁");
    					//此时B线程已经执行完成了,但是A线程任然还在等待,因此需要唤醒A线程
    					"A".notify();
    				}
    			}
    		};
    		
    		Thread t1=new Thread(runnable1);
    		Thread t2=new Thread(runnable2);
    		
    		t1.start();
    		t2.start();
    	}
    }
    /*输出结果:
    A线程持有了A锁,等待B锁
    B线程持有了B锁,等待A锁
    B线程持有了A锁和B锁
    A线程持有了A锁和B锁
    */
    

    方式2:Lock锁下的通信,采用Condition控制通信。JUC中的类(java.util.comcurrent类)

    • await():等价于wait()
    • signal():等价于notify()
    • signalAll():等价于notifyAll()

    4、多线程下的单例类

    懒汉式单例类会出现问题

    //定义一个单例类
    class Boss{
    	//构造器私有化
    	private Boss() {
    		System.out.println("一个Boss对象被实例化了");
    	}
    	private static Boss instance=null;
    	//外部类只能通过该方法获取Boss类的实例
    	public static Boss getBoss() {
    		if(instance==null) {
    			instance=new Boss();
    		}
    		return instance;
    	}
    }
    public class SingletonTest {
    	public static void main(String[] args) {
    		Runnable runnable=()->{
    			Boss.getBoss();
    		};
    		//开辟了100条线程去获取这Boss实例
    		for(int i=0;i<100;i++) {
    			new Thread(runnable).start();
    		}
    	}
    }
    

    当多线程去执行这个单例类时,还是希望只产生一个实例对象,但程序输出结果明显不是,这是由于多线程导致的。

    修改方式1:对临界资源上锁,使用同步代码

    //定义一个单例类
    class Boss{
    	//构造器私有化
    	private Boss() {
    		System.out.println("一个Boss对象被实例化了");
    	}
    	private static Boss instance=null;
    
    	public static Boss getBoss() {
            //同步代码段
    		synchronized("") {
    			if(instance==null) {
    				instance=new Boss();
    			}
    		}
    		return instance;
    	}
    
    }
    
    public class SingletonTest {
    	public static void main(String[] args) {
    		Runnable runnable=()->{
    			Boss.getBoss();
    		};
    		//开辟了100条线程去获取这Boss实例
    		for(int i=0;i<100;i++) {
    			new Thread(runnable).start();
    		}
    	}
    }
    

    修改方式2:对临界资源上锁,使用同步方法

    class Boss{
    	//构造器私有化
    	private Boss() {
    		System.out.println("一个Boss对象被实例化了");
    	}
    	private static Boss instance=null;
    	//同步方法
    	public static synchronized Boss getBoss() {
    		if(instance==null) {
    			instance=new Boss();
    		}
    		return instance;
    	}
    }
    
    public class SingletonTest {
    	public static void main(String[] args) {
    		Runnable runnable=()->{
    			Boss.getBoss();
    		};
    		//开辟了100条线程去获取这Boss实例
    		for(int i=0;i<100;i++) {
    			new Thread(runnable).start();
    		}
    	}
    }
    

    六、线程池

    线程池在系统启动时就创建大量空闲的线程。提前创建多个线程,放入线程池,使用时直接从线程池中获取,使用完放回池中

    1、作用

    可以避免频繁创建销毁线程的过程,实现充分利用

    • corePoolSize:核心池的大小(可以放多少个线程)
    • maximumPoolSize:最大线程数(一次可以同时运行的线程数量)
    • keepAliveTime:线程没有任务时最多保持多长时间后会终止

    2、创建方式

    • ExecutorService接口:线程池真正的接口
    • Executor:创建线程的工具类,调用该类的newFixedThreadPool(corePoolSizesize)方法来创建线程池
    • execute:执行Runnable接口的,无返回值
    • Future submit:执行Callable接口的,有返回值
    • shutdown:关闭连接
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ThreadPoolTest {
    	public static void main(String[] args) {
    		Runnable r=()->{
    			System.out.println(Thread.currentThread().getName());
    		};
    		//创建线程池,设置大小为10
    		ExecutorService service=Executors.newFixedThreadPool(10);
    		//执行
    		service.execute(r);
    		service.execute(r);
    		service.execute(r);
    		service.execute(r);
            //关闭连接
    		service.shutdown();
    	}
    }
    /*输出结果:
    pool-1-thread-3
    pool-1-thread-4
    pool-1-thread-2
    pool-1-thread-1
    */
    

    七、JUC组件

    1、未来任务FutureTask

    利用Callable创建线程时,有返回值,该值由Future进行封装,FutureTask实现了RunnableFuture接口,而该接口继承自Runnable和Future接口,因此FutureTask既可以当做一个任务执行,也可以有返回值。

    当计算一个任务需要很长时间时,可使用FutureTask来封装这个任务,使得主线程在完成自己的任务后在去获取这个计算结果

    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class FutureTaskTest {
    	public static void main(String[] args) {
    		//创建一个Clallable接口,有返回值,给子线程执行
    		Callable<Integer> cla=()->{
    			int result=0;
    			for(int i=0;i<100;i++) {
    				Thread.sleep(10);  //每一次计算时都让让主线程执行一段时间
    				result+=i;
    			}
    			return result;
    		};
    		//新建一个FutureTask实例
    		FutureTask<Integer> futureTask=new FutureTask<>(cla);
    		//执行计算任务的线程
    		Thread t1=new Thread(futureTask);
    		t1.start();
    		
            //创建Runnable接口,给主线程执行
    		Runnable runnable=()->{
    			System.out.println("主线程任务正在执行");
    			try {
    				Thread.sleep(10);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		};
    		
    		Thread t2=new Thread(runnable);
    		t2.start();
    		
    		//得到有返回值的输出
    		try {
    			System.out.println(futureTask.get());
    		} catch (InterruptedException | ExecutionException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    }
    /*输出结果:
    另一个线程任务正在执行
    4950
    */
    //如果将Callable执行体中的Thread.sleep(10);去掉,则执行结果为:4950  另一个线程任务正在执行。
    

    2、阻塞队列BlockingQueue

    利用BlockingQueue作为线程同步的工具,主要用来实现消费者生产者设计模式。详见《生产者消费者设计模式》

    3、叉链接ForkJoin

    主要用于并行计算中,将大的任务分成小的任务进行计算,再把小任务的结果合并成总的计算结果

    展开全文
  • 文章目录进程线程、多线程并发、并行#1 进程#2 线程#3 多进程#4 多线程#5 并发#6 并行#7 通过多线程实现并发,并行 进程线程、多线程并发、并行 首先,并行与并发都是程序多线程处理场景,因此,一旦提到...
  • 实验三 观察Linux进程线程的异步并发执行 一、实验目的 通过本实验学习如何创建Linux进程线程,通过实验,观察Linux进程线程的异步执行。理解进程线程的区别及特性,进一步理解进程是资源分配单位,线程是独立...
  • 进程线程并发执行的概念

    千次阅读 2017-11-16 15:29:07
    进程线程并发执行的概念,我们先来看下面的一段话:“一般来说,当运行一个应用程序的时候,就启动了一个进程,当然有些会启动多个进程。启动进程的时候,操作系统会为进程分配资源,其中最主要的资源是内存...
  • 【多线程并发编程】一进程线程(并发和并行)

    千次阅读 多人点赞 2020-02-15 19:45:35
    了解并发和并行,进程线程的一些概念,更好的学习多线程编程
  • Java的多线程与高并发

    千次阅读 2021-11-22 17:20:53
    线程是系统调度的最小单元,是线程的运算单元,一个进程可以包括一个或多个线程线程的三种创建方式: 一、继承Thread类 class MyThread extends Thread { @Override public void run() { Log.i(TAG, ...
  • 在疫情期间以及未来毕业后即将工作的憧憬,给自己立个学习flag,学习并第一次通过CSDN记录下C++11的多线程并发编程。初学者的我写的不好,还望大家多多指正与批评, 学习多线程并发编程首先一定得熟悉并发相关概念...
  • Java通过对多线程的支持来在一个进程并发执行多个线程,每个线程都并行执行不同的任务。 二、线程创建方式 一共四种方式:继承Thread类、实现Runnable接口、ExecutorService和Call(有返回Class类型值)、基于...
  • 线程&并发-实例与解决方案

    千次阅读 2020-06-23 11:27:24
    Java的线程状态被定义在公共枚举类java.lang.Thread.state。一种有六种状态 1.新建(NEW):表示线程新建出来还没有被启动的状态,比如:Thread t = new MyThread(); 2.就绪/运行(RUNNABLE):该状态包含了经典...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 587,028
精华内容 234,811
关键字:

不同进程中的线程可以并发