JAVA并发编程

1.什么是线程和进程?

        进程:进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建、运行到消亡的过程。

        线程:线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享进程的堆和方法与资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈。

        在Java中,线程作为最小调度单位,进程作为资源分配的最小单位。在windows中进程是不活动的,只是作为线程的容器。

2.创建线程的方法?

3.线程使用中常见方法?

        1.sleep()和yield():前者会让当前线程从Running进入Timed Waiting(阻塞)状态,其它线程可以使用interrupt方法打断正在睡眠的线程,sleep方法会抛出InterruptedException,睡眠结束后的线程未必会立刻得到执行;调用yield会让当前线程从Running状态进入Runnable就绪状态,然后调度执行其他线程,具体的实现依赖于操作系统的任务调度器。

        2.join()方法:t1.join()表示需要等待名为t1的线程执行完毕,可以在其中设置时间参数,指定等待的时间。但是如果等待时间设置过长,只要t1线程执行完毕就会继续执行主线程中的内容,不会一直等待。

4.守护线程

        Java中默认所有线程都结束运行,整个进程才会结束,不过守护线程是例外,如果只有守护线程还在运行,整个进程也会结束。

        垃圾回收器线程就是一种守护线程;Tomcat中的Acceptor和Poller线程都是守护线程,所以Tomcat接收到shutdown命令后,不会等待它们处理完当前请求。

5.线程状态

        1.初始状态:仅仅在语言层面创建了线程对象,还未与操作系统线程关联;

        2.可运行状态:(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由CPU调度执行;

        3.运行状态:指获取了CPU时间片运行中的状态;

        4.阻塞状态:如果调用了阻塞API,如BIO读写文件,这时该线程实际不会用到CPU,会导致线程上下文切换,进入阻塞状态。等BIO操作完毕,会由操作系统唤醒阻塞的线程,转换至可运行状态。与可运行状态的区别是,对阻塞状态的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们。

        5.终止状态:表示线程已经执行完毕,生命周期已经结束,不再转换为其他状态。

6.sleep(long n)和wait(long n)的区别?

        1.sleep是Thread中的方法,而wait是Object中的方法;

        2.sleep不需要强制和synchronized配合使用,但wait需要和synchronized一起用;

        3.sleep在睡眠的同时,不会释放对象锁,但wait在等待的时候会释放对象锁;

        4.不过二者设置后的状态都是TIMED_WAITING;

7.join()方法的底层原理?

        1.首先要明确join方法是要等待调用的线程执行完毕

public final synchronized void join(long millis) throws InterruptedException{
    long base = System.currentTimeMillis();
    long now = 0;
    if(mills < 0){
        throw new IllegalArgumentException("timeout value is negative");
    }
    if(mills == 0){
        while(isAlive()){
            wait(0);
        }
    }else{
        while(isAlive()){
            long delay = millis - now;
            if(delay <= 0){
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

无参的join方法会一直等待,有参的join方法,不允许传递负数;参数为0时,调用的是wait方法;参数大于0时,先记录当前时间base和已经等待的时间now,只要线程存活,循环计算还需等待的时间delay,如果delay<=0,则跳出循环停止等待,否则,继续等待delay的时间,每次要多已经等待的时间now进行更新,这样设计避免了被虚假唤醒时,之前等待的时间作废的情况;

8.线程各状态之间的转换?

        1.new---->runnable

                当调用t.start()方法时,由new--->runnable

        2.runnable <---->waiting

                t线程用synchronized(obj)获取了对象锁后

                调用obj.wait()方法时,t线程从runnable--->waiting

                调用obj.notify(), obj.notifyAll(), t.interrupt()时,如果竞争锁成功,t线程从waiting---        >runnable, 竞争锁失败,t线程从waiting--->blocked

        3.runnable<--->waiting

                当前线程调用t.join()方法时,当前线程从runnable--->waiting,注意是当前线程在t线程对          象的监视器上等待;t线程运行结束,或调用le当前线程的interrup()时,当前线程从waiti---        >runnable。

        4.runnable<--->waiting

                当前线程调用LockSupport.park()方法会让当前线程从runnable--->waiting,调用LockSupport.unpark(目标线程)或调用了线程的interrupt(),会让目标线程从waiting--->runnable

        5.runnable<--->timed_waiting

                t线程调用synchornized(obj)获取了对象锁后

                调用obj.wait(long n)方法时,t线程从runnable--->timed_waiting

                 t线程等待时间超过了n毫秒,或调用obj.notify(), obj.notifyAll(), t.interrupt()时,竞争锁            成功,t线程从timed_waiting--->runnable, 竞争锁失败,t线程从timed_waiting--->blocked

        6.runnable<--->timed_waiting

                当前线程调用t.join(long n)方法时,当前线程从runnable--->timed_waiting,注意是当前线程在t线程对象的监视器上等待

                当前线程等待时间超过了n毫秒,或t线程运行结束,或调用了当前线程的interrupt()时,当前线程从timed_waiting--->runnable

        7.runnable<--->timed_waiting

                当前线程调用Thread.sleep(long n),当前线程从runnable--->timed_waiting,当前线程等待时间超过了n毫秒,当前线程从timed_waiting--->runnable

        8.runnable<--->timed_waiting

                当前线程调用LockSupport.parkNanos(long nanos)或LockSupport.parkUntil(long millis)时,当前线程从runnable--->timed_waiting,调用LockSupport.unpark(目标线程)或调用了线程的interrupt(),或是等待超时,会让目标线程从timed_waiting--->runnable

        9.runnable<--->blocked

                t线程用synchronized(obj)获取了对象锁时如果竞争失败,从runnable--->blocked

持有obj锁线程的同步代码块执行完毕,会唤醒对象上所有blocked的线程重新竞争,如果其中t线程竞争成功,从blocked--->runnable,其他失败的线程仍然blocked.

9.volatile(易变关键字)

        它可以用来修饰成员变量和静态成员变量,可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作volatile变量都是直接操作主存。

        volatile的底层实现原理是内存屏障,Memory Barrier,对volatile变量的写指令后会加入写屏障,对volatile变量的读指令前会加入读屏障。

10.线程池

1.线程池状态:ThreadPoolExecutor使用int的高3位来表示线程池状态,低29位表示线程数量。

状态名高三位接收新任务处理阻塞队列任务说明
RUNNING111NY
SHUTDOWN000NY不会接受新任务,但会处理阻塞队列剩余任务
STOP001NN会中断正在执行的任务,并抛弃阻塞队列任务
TIDYING010任务全执行完毕,活动线程为0即将进入终结
TERMINATED011终结状态

这些信息存储在一个原子变量ctl中,目的是将线程池状态与线程个数合二为一,这样就可以利用一次cas原子操作进行赋值。

2.构造方法

public ThreadPoolExecutor(int corePoolSize,
                            int maximumPoolSize,
                            long keepAliveTime,
                            TimeUnit unit,
                            BlockingQueue<Runnable> workQueue,
                            ThreadFactory threadFactory,
                            RejectedExecutionHandler handler)

corePoolSize:核心线程数目(最多保留的线程数)

maximumPoolSize:最大线程数目

keepAliveTime:生存时间---针对救急线程

unit:时间单位---针对救急线程

workQueue:阻塞队列

threadFactory:线程工厂---可以为线程创建时起个好名字

handler:拒绝策略

救急线程 = 最大线程数 - 核心线程数

2.1

      线程池刚开始没有线程,当一个任务提交给线程池后,线程池会创建一个新线程来执行任务;(不过可以通过设置prestartAllCoreThreads可以预创建核心线程)

      当线程数达到corePoolSize并且没有线程空闲,这时如果再加入任务,新加入的任务会被加入workQueue队列排队,直到有空闲的线程。

        如果队列选择了有界队列,那么任务超过了队列大小时,会创建maximumPoolSize - corePoolSize数目的线程来救急。

        如果线程到达maximumPoolSize仍然有新任务这时会执行拒绝策略。拒绝策略jdk提供了4种实现,其它著名框架也提供了实现

        AbortPolicy让调用者抛出RejectedExecutionException异常,这是默认策略,适用于必须通知调用者任务未能被执行的场景;

        CallerRunsPolicy让调用者线程自身运行任务,适用于希望通过减缓任务提交速度来稳定系统的场景;

        DiscardPolicy放弃本次任务,不会执行任何操作,也不会抛出任何异常。适用于对部分任务丢弃没有影响的场景,或系统负载较高时不需要处理所有任务;

        DiscardOldestPolicy放弃队列中最早的任务,本任务取而代之,适用于希望丢弃最旧的任务以保证新的重要人物能够被处理的场景;

        Dubbo的实现,在抛出RejectedExecutionException异常之前会记录日志,并dump线程栈信息,方便定位问题

        Netty的实现,是创建一个新线程来执行任务

        ActiveMQ的实现,带超时等待(60s)尝试放入队列,类似我们之前自定义的拒绝策略

        PinPoint的实现,它使用了一个拒绝策略链,会逐一尝试策略链中每种拒绝策略

       当高峰过去后,超过corePoolSize的救急线程如果一段时间没有任务做,需要结束节省资源,这个时间由keepAliveTime和unit来控制

11.Java中线程之间如何进行通信?

        线程之间的通信主要依赖于共享内存。由于线程共享同一个进程的内存空间,因此可以直接通过共享变量进行通信;

       1)共享变量:线程可以通过访问共享内存来交换信息(需要注意同步问题,防止数据竞争和不一致),共享的也可以是文件,例如写入同一个文件来进行通信;

        2)同步机制:synchronized:Java中的同步关键字,用于确保同一时刻只有一个线程可以访问共享资源,利用Object类提供的wait(),notify(),notifyAll()实现线程之间的等待、通知机制;    reentrantlock:配合condition提供了类似于wait(),notify()的等待通知机制

blockingqueue:通过阻塞队列实现生产者-消费者模式 ;  countdownlatch:可以允许一个或者多个线程等待,直到在其他线程中执行的一组操作完成;     volatile:Java中的关键字,确保变量的可见性,防止指令重排;   semaphore:信号量,可以控制对特定资源的访问线程数;

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值