多线程与高并发基础(基础篇一)

多线程与高并发基础

一、程序、进程、线程、纤程(协程)的定义
程序:可执行文件
进程:程序中资源分配的最小单元
纤程:程序中资源调度的最小单元
携程:用户管理的纤程
计算机组成原理在这里插入图片描述
你必须知道的几个概念
1、同步&异步
2、并行&并发
并行:真正意义上的同时执行
并发:偏重于多个任务交替执行(cpu是交替执行,所以有高并发一说)
比如登山时,地形险峻,路面湿滑,导游要求走路不看景,看景不足路,这种看会风景走一会的方式就是并发。
但是坐缆车登山观景,登山和看景可以同时进行,这就是并行。
3、临界区
临界区用来表示一种公共资源或者说共享数据,可以被多线程使用。但是每一次只有一个线程使用它,一旦临界区资源被占用,其他线程想要使用这个资源,就必须等待。比如办公室的打印机,
在并行程序中,临界区的资源是保护的对象,如果意外出现打印机同时打印两个打印任务,那么最有可能的结果就是打印出来的文件是损坏的文件。
4、阻塞&非阻塞
一个线程占用了临界资源,那么其他所有需要这个资源的线程都必须在这个临界区中进行等待。等待会导致线程挂起,这种操作就是阻塞。
5、死锁&饥饿&活锁
死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

死锁发生在当一些进程请求其他进程占有的资源而被阻塞时。

死锁发生的四个条件
1、互斥条件:线程对资源的访问是排他性的,如果一个线程对占用了某资源,那么其他线程必须处于等待状态,直到资源被释放。
2、请求和保持条件:线程T1至少已经保持了一个资源R1占用,但又提出对另一个资源R2请求,而此时,资源R2被其他线程T2占用,于是该线程T1也必须等待,但又对自己保持的资源R1不释放。
3、不剥夺条件:线程已获得的资源,在未使用完之前,不能被其他线程剥夺,只能在使用完以后由自己释放。
4、环路等待条件:在死锁发生时,必然存在一个“进程-资源环形链”,即:{p0,p1,p2,…pn},进程p0(或线程)等待p1占用的资源,p1等待p2占用的资源,pn等待p0占用的资源。(最直观的理解是,p0等待p1占用的资源,而p1而在等待p0占用的资源,于是两个进程就相互等待)

活锁:是指线程1可以使用资源,但它很礼貌,让其他线程先使用资源,线程2也可以使用资源,但它很绅士,也让其他线程先使用资源。这样你让我,我让你,最后两个线程都无法使用资源。
活锁不会被阻塞,而是不停检测一个永远不可能为真的条件。除去进程本身持有的资源外,活锁状态的进程会持续耗费宝贵的CPU时间。

关于“死锁与活锁”的比喻:
死锁:迎面开来的汽车A和汽车B过马路,汽车A得到了半条路的资源(满足死锁发生条件1:资源访问是排他性的,我占了路你就不能上来,除非你爬我头上去),汽车B占了汽车A的另外半条路的资源,A想过去必须请求另一半被B占用的道路(死锁发生条件2:必须整条车身的空间才能开过去,我已经占了一半,尼玛另一半的路被B占用了),B若想过去也必须等待A让路,A是辆兰博基尼,B是开奇瑞QQ的屌丝,A素质比较低开窗对B狂骂:快给老子让开,B很生气,你妈逼的,老子就不让(死锁发生条件3:在未使用完资源前,不能被其他线程剥夺),于是两者相互僵持一个都走不了(死锁发生条件4:环路等待条件),而且导致整条道上的后续车辆也走不了。
例如:马路中间有条小桥,只能容纳一辆车经过,桥两头开来两辆车A和B,A比较礼貌,示意B先过,B也比较礼貌,示意A先过,结果两人一直谦让谁也过不去。

饥饿:线程因某种原因(优先级)一直无法获取到所需的资源。例如雏鸟喂食,弱小的一直抢不到食物。

二、单核系统做多线程设计是否有意义

纤程分为io密集型和CPU密集型,对于IO密集型纤程,cpu多数时间处于空闲状态,适当的多线程设计可提供cpu利用率,充分压榨cpu性能,提高处理能力。

三、是不是线程数设计越多越好?
不是
cpu只负责操作指令的执行,操作系统负责任务的调度。OS在多个线程间切换会消耗一定的性能,线程数过度会消耗过多性能在线程切换上,反而会降低系统性能。

import java.text.DecimalFormat;
import java.util.Random;
import java.util.concurrent.CountDownLatch;

public class Test_ContextSwitch {
    private static double[] nums=new double[1_0000_0000];
    private static Random r=new Random( );
    private static DecimalFormat df=new DecimalFormat("0.00");
    static {
        for (int i = 0; i < nums.length; i++) {
            nums[i]=r.nextDouble();
        }
    }

    private static void m1(){
        long startTime=System.currentTimeMillis();
        double result=0.0;
        for (int i = 0; i < nums.length; i++) {
            result+=nums[i];
        }
        long endTime=System.currentTimeMillis();
        System.out.println("m1: "+(endTime-startTime)+" result = "+df.format(result));
    }


    //2个线程实现
    static double result1=0.0,result2=0.0,result=0.0;
    private static void m2() throws Exception{
        Thread t1=new Thread(()->{
            for (int i = 0; i < nums.length/2; i++) {
                result1+=nums[i];
            }
        });
        Thread t2=new Thread(()->{
            for (int i = nums.length/2; i < nums.length; i++) {
                result2+=nums[i];
            }
        });
        long startTime=System.currentTimeMillis();
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        result=result1+result2;
        long endTime=System.currentTimeMillis();
        System.out.println("m2:"+(endTime-startTime)+" result = "+df.format(result));
    }

    private static void m3(int number) throws Exception{
        final int threadCount;
        threadCount=number>0? number:1000;
        Thread[] threads=new Thread[threadCount];
        double[] results=new double[threadCount];
        final int segmentCount=nums.length/threadCount;
        CountDownLatch latch=new CountDownLatch(threadCount);

        for (int i = 0; i < threadCount; i++) {
            int m=i;
            threads[i]=new Thread(()->{
                for (int j = m*segmentCount; j < (m+1)*segmentCount&&j<nums.length; j++) {
                    results[m]+=nums[j];
                }
            });
            latch.countDown();
        }

        double resultM3=0.0;
        long startTime=System.currentTimeMillis();
        for (Thread t : threads) {
            t.start();
        }
        latch.await();
        for (int i = 0; i < results.length; i++) {
            resultM3+=results[i];
        }
        long endTime=System.currentTimeMillis();
        System.out.println("m3:"+(endTime-startTime)+" resultM3 = "+df.format(result));
    }

    public static void main(String[] args) throws Exception{
        m1();//单线程执行
        m2();//启动2个线程执行
        m3(10000);//自定n个线程执行(int)
    }
}


"D:\Program Files\Java\jdk1.8.0_201\bin\java" "...
m1: 1135 result = 50001208.95
m2:2176 result = 50001208.95
m3:2785 resultM3 = 50001208.95

Process finished with exit code 0

四、线程数如何设计才是最合理的?
线程数=cpu核数cpu利用率*[任务执行时间/(任务执行时间-IO等待时间)]
举例:某线程等待时间为50%,期望cpu利用率为50%,cpu核数为32核。则:线程数=32* 0.5*[1/(1-0.5)]=32

五、是不是cpu利用率越高越好?
不是
因为不管在什么环境下运行一个java程序,都会有其他线程存在(比如gc),不可能该线程完全独享cpu资源。而且在实际开发中,我们需要空闲出部分资源防止突发情况。

六、如何确定线程的IO等待时间?
使用profiler工具

七、创建线程的几种方式
1、 直接继承自Thread
2、实现Runnable接口
3、使用Lambda表达式创建(本质和m1是一样的)
4、使用线程池方式实现Callable newCachedThreadPool
5、使用FutureTask实现Callable
6、使用线程池方式,通过Lambda表达式创建
7、使用线程池方式实现Callable newFixedThreadPool
8、使用线程池方式实现Callable newSingleThreadExecutor
9、使用线程池方式实现Callable newScheduledThreadPool

package com.example.mythreadtest;

import java.util.concurrent.*;

/**
 * @Auther wangdongdong
 * @Date 2021/7/19  上午 10:30
 * @Description
 * @Verson 1.0
 */
public class CreateThread {
    public static void main(String[] args) {
        m0();  //直接继承自Thread
        m1();  //实现Runnable接口
        m2();  //使用Lambda表达式创建(本质和m1是一样的)
        m3();  //使用线程池方式实现Callable         newCachedThreadPool
        m4();  //使用FutureTask实现Callable
        m5();  //使用线程池方式,通过Lambda表达式创建
        m6();  //使用线程池方式实现Callable        newFixedThreadPool
        m7();  //使用线程池方式实现Callable        newSingleThreadExecutor
        m8();  //使用线程池方式实现Callable        newScheduledThreadPool

    }
    
    直接继承自Thread
    public static void m0(){
        new myThread().start();
    }

    //实现Runnable接口
    public static void m1(){
        new Thread(new myRunable()).start();
    }


    //使用Lambda表达式创建(本质和m1是一样的)
    public static void m2(){
        new Thread(()->{
            System.out.println("m2: This is my Thread user Lambda!");
        }).start();
    }

    //使用线程池方式实现Callable
    public static String m3(){
        String ret="";
        ExecutorService service=Executors.newCachedThreadPool();
        Future<String> f = service.submit(new myCallable());
        try {
            ret=f.get();
            System.out.println("m3: "+ret);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }finally {
            service.shutdown();
        }
        return ret;
    }

    //使用FutureTask实现Callable
    /*
    工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
    如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。
    在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
     */
    public static String m4(){
        String ret="";
        FutureTask<String> ft=new FutureTask<>(new myCallable());
        Thread td=new Thread(ft);
        td.start();
        try {
            ret=ft.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("m4: "+ret);
        return ret;
    }

    //使用线程池方式,通过Lambda表达式创建
    public static void m5(){
        ExecutorService service=Executors.newCachedThreadPool();
        service.execute(()->{
            System.out.println("m5: This is my Thread user cachedThreadPool!");
        });
       service.shutdown();
    }

    //使用线程池方式,newFixedThreadPool
    /*
      创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
      FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,
      它不会释放工作线程,还会占用一定的系统资源。
     */
    public static String m6(){
        String ret="";
        ExecutorService service=Executors.newFixedThreadPool(3);
        Future<String> f = service.submit(new myCallable());
        try {
            ret=f.get();
            System.out.println("m6: "+ret);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }finally {
            service.shutdown();
        }
        return ret;
    }

    //使用线程池方式,newSingleThreadExecutor
    /*
      创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,
      保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。
      单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。
     */
    public static String m7(){
        String ret="";
        ExecutorService service=Executors.newSingleThreadExecutor();
        Future<String> f = service.submit(new myCallable());
        try {
            ret=f.get();
            System.out.println("m7: "+ret);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }finally {
            service.shutdown();
        }
        return ret;
    }

    //使用线程池方式,newScheduleThreadPool
    /*
      创建一个定长的线程池,而且支持定时的以及周期性的任务执行
     */
    public static void m8(){
        String ret="";
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        scheduledThreadPool.schedule(new Runnable() {
            public void run() {
                System.out.println("m8: This is my thread extend Thread,delay 3 seconds !");
            }
        }, 3, TimeUnit.SECONDS);

        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
            public void run() {
                //延时一秒,没3秒执行一次
                System.out.println("m8: delay 1 seconds, and excute every 3 seconds");
            }
        }, 1, 3, TimeUnit.SECONDS);
    }

}
class  myThread extends Thread{
    @Override
    public void run() {
        System.out.println("m0: This is my thread extend Thread !");
    }
}

class myRunable implements Runnable{

    @Override
    public void run() {
        System.out.println("m1: This is my runable implements Runnable !");
    }
}

class myCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "This is my callable implements Callable !";
    }
}

"D:\Program Files\Java\jdk1.8.0_201\bin\java" "...
m1: This is my runable implements Runnable !
m0: This is my thread extend Thread !
m2: This is my Thread user Lambda!
m3: This is my callable implements Callable !
m4: This is my callable implements Callable !
m5: This is my Thread user cachedThreadPool!
m6: This is my callable implements Callable !
m7: This is my callable implements Callable !
m8: delay 1 seconds, and excute every 3 seconds
m8: This is my thread extend Thread,delay 3 seconds !
m8: delay 1 seconds, and excute every 3 seconds

Process finished with exit code -1

八、线程的状态
1、new
2、runnable(ready&running)
3、waiting
4、timed waiting
5、blocked
6、terminated

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
/**
 * 代码打印6种状态
 * 1: NEW
 * 2: RUNNABLE
 * 3: TERMINATED
 * 4: WAITING
 * 5: TIMED_WAITING
 * 6: BLOCKED
 */
public class Test_ThreadStatus {
    public static void main(String[] args) throws Exception {
        Thread t1=new Thread(()->{
            //线程被执行
            System.out.println("2: "+Thread.currentThread().getState());
            for (int i = 0; i < 3; i++) {
                SleepHelper.sleepSeconds(1);
            }
        });
        //线程刚创建,未被执行
        System.out.println("1: "+t1.getState());
        t1.start();
        t1.join();
        //线程执行结束
        System.out.println("3: "+t1.getState());


        //========================================================================
        Thread t2=new Thread(()->{
            try {
                LockSupport.park();//线程等待被唤醒
                TimeUnit.SECONDS.sleep(5);
            }catch (Exception e){
                e.printStackTrace();
            }
        });

        t2.start();
        TimeUnit.SECONDS.sleep(1);  //保证线程已经被park()
        System.out.println("4: "+t2.getState());

        LockSupport.unpark(t2);
        TimeUnit.SECONDS.sleep(1);//确保1线程已经被1叫醒
        System.out.println("5: "+t2.getState());//在执行sleep(5)方法


        //========================================================================
        final Object o=new Object();
        Thread t3=new Thread(()->{
            synchronized (o){
                SleepHelper.sleepSeconds(1);
            }
        });

        new Thread(()->{
            synchronized (o){
                SleepHelper.sleepSeconds(5);
            }
        }).start();//使当前线程先抢占到锁
        SleepHelper.sleepSeconds(1);
        t3.start();
        SleepHelper.sleepSeconds(1);
        System.out.println("6: "+t3.getState());//处于阻塞状态
    }
}

class SleepHelper{
    //避免代码里多次try、catch
    public static void sleepSeconds(int seconds){
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

:使用synchronized会进入阻塞状态(block),使用锁会进入等待(waiting)状态

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

public class Test_ThreadStatus {
    public static void main(String[] args) throws Exception {
        //注:使用synchronized会进入阻塞状态(block),使用锁会进入等待(waiting)状态
        final Lock lock=new ReentrantLock();
        Thread t4=new Thread(()->{
            lock.lock();
            SleepHelper.sleepSeconds(1);
            lock.unlock();
        });

        new Thread(()->{
            lock.lock();
            SleepHelper.sleepSeconds(5);
            lock.unlock();
        }).start();

        SleepHelper.sleepSeconds(1);
        t4.start();
        SleepHelper.sleepSeconds(1);
        System.out.println("7:"+t4.getState());//结果输出   7:WAITING
    }
}

class SleepHelper{
    //避免代码里多次try、catch
    public static void sleepSeconds(int seconds){
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值