多线程编程,处理多线程的并发问题(线程池)

        线程对象是可以产生线程的对象。比如在 Java 平台中Thread对象,Runnable对象。线程,是指正在执行的一个指点令序列。在java平台上是指从一个线程对象的start()开始,运行run方法体中的那一段相对独立的过程。相比于多进程,多线程的优势有:
    (1)进程之间不能共享数据,线程可以;
    (2)系统创建进程需要为该进程重新分配系统资源,故创建线程代价比较小;
    (3)Java语言内置了多线程功能支持,简化了java多线程编程。
  1、线程实现方式:
             * 第一种方式:
             *         1.自定义类,继承Thread;
             *         2.重写run( )方法;
             *         3.启动线程:
             *             1).实例化自定义线程对象;
             *             2).调用自定义线程对象的start()方法启动线程;
             *
             * 第二种方式:
             *         1.自定义类,实现Runnable接口;
             *         2.重写run( )方法:
             *         3.启动线程:
             *             1).实例化自定义的Runnable子类对象;
             *             2).实例化一个Thread对象,将自定义对象作为参数传给Thread的构造方法;
             *             3).调用Thread对象的start()启动线程;

                区别:
                        一种是扩展java.lang.Thread类      
                        另一种是实现java.lang.Runnable接口
                好处:
                        在实际开发中通常以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类可以避免继承的局限,一个类可以继承多个接口,适合于资源的共享。

            线程和进程的区别:
                  进程:
                       每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,
                        一个进程包含1--n个线程。
                      (进程是资源分配的最小单位)。
                线程:
                        同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。
                      (线程是cpu调度的最小单位)。

        2、多线程
            好处:
                1.可以充分利用CPU资源;
                2.对于线程中代码,可以独立于"主线程"单独运行,它不用等待前面的代码执行完毕;
                   也就意味着,我们的程序可以同时做多件事情;

            问题:
                1.多线程有几种实现方案,分别是哪几种?
                     *  三种:
                     *     1.继承Thread,重写run()方法;
                     *     2.实现Runnable接口,重写run()方法;
                     *     3.(JDK5)实现Callable接口,重写call()方法;
                     *
                    
                3.启动一个线程是run()还是start()?它们的区别?
                    1).start()启动线程;
                    2).run是普通方法,我们把线程中需要做的事情写在这里;
                       start方法用于启动线程,它会调用run()方法;
                       
                4.sleep( )和wait( )方法的区别:
                    1).sleep:
                        a.属于Thread类;
                        b.让当前线程休眠指定的时间,当时间到,会自动醒来;
                        c.在同步方法中,不会释放锁;
                    2).wait:
                        a.属于Object类;
                        b.可以指定时间,也可以不指定;当时间到时,或者使用notify()或notifyAll()方法后,会醒来;
                        c.在同步方法中,会释放锁;
                        
                5.为什么wait( ),notify( ),notifyAll( )等方法都定义在Object类中:
                    1).因为任何对象都有可能被多线程并发访问,所以,任何对象都有让当前访问的线程"等待"的权利,也有需要"唤醒"的义务;
                    
                6.线程的生命周期
                    新建:
                          用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态。
                          处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable)。
                    就绪:
                         处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,处于线程就绪队列,
                         等待系统为其分配CPU。
                         一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。
                    运行:
                         处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
                    阻塞:
                         处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待IO设备等资源,
                         将让出CPU并暂时停止自己的运行,进入阻塞状态。在阻塞状态的线程不能进入就绪队列。
                    死亡:
                        当线程的run()方法执行完,或者被强制性地终止,就认为它死去。
                        
                7."并行"与"并发":
                      1)."并行":是指多个线程在"某个时间段内",在同时运行;
                      2)."并发":是指多个线程在"某个时间点上",同时访问一个共享资源;

        3、并发
             定义:
                   是指多个线程在"某个时间点上",同时访问一个共享资源;

             同步的方式:
                    1.synchronized:
                            1).同步代码块;
                                       即有synchronized关键字修饰的语句块。 
                                       被该关键字修饰的语句块会自动被加上内置锁,从而实现同步 
                                       注:
                                              同步是一种高开销的操作,因此应该尽量减少同步的内容。 
                                              通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
 
                            2).同步方法;
                                      即有synchronized关键字修饰的方法。 
                                      由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 
                                      内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
                  2.(JDK5) ReentrantLock锁:
                             在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。
                             ReentrantLock类是可重入、互斥、实现了Lock接口的锁, 
                             它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。
                       常用方法有:
                                 ReentrantLock() : 创建一个ReentrantLock实例 
                                 lock() : 获得锁 
                                 unlock() : 释放锁 
                       注意:
                            ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用 
                       例子:              
                            // 存钱
                            public void addMoney(int money) {
                                    lock.lock();//上锁
                            try{
                                count += money;
                                System.out.println(System.currentTimeMillis() + "存进:" + money);

                                }finally{
                                        lock.unlock();//解锁 }
                                }
                   3.使用特殊域变量(volatile)实现线程同步      
                             a.volatile关键字为域变量的访问提供了一种免锁机制, 
                             b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新, 
                             c.因此每次使用该域就要重新计算,而不是使用寄存器中的值 
                             d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量 

                    好处:解决了并发性访问的问题;
                    弊端:由于要处理线程的等待、阻塞等问题,所以效率会降低;    

        4、线程池
            1).获取线程池对象:JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法:
                    public static ExecutorService newCachedThreadPool():创建一个可根据需要创建新线程的线程池
                    public static ExecutorService newFixedThreadPool(int nThreads):创建一个可重用固定线程数的线程池
                    public static ExecutorService newSingleThreadExecutor():创建一个使用单个 worker 线程的 Executor
            
            2).操作线城池:
                这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。
                它提供了如下方法
                        Future<?> submit(Runnable task)
                        <T> Future<T> submit(Callable<T> task)

            3).使用线程池的好处
                      第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
                      第二:提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
                      第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,
                               还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
                               但是要做到合理的利用线程池,必须对其原理了如指掌。

             4).线程池的处理流程如下    
  1.                     首先线程池判断基本线程池是否已满?没满,创建一个工作线程来执行任务。满了,则进入下个流程。
  2.                     其次线程池判断工作队列是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程。
  3.                    最后线程池判断整个线程池是否已满?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务。

                  5) .线程池的组成部分
                         一个比较简单的线程池至少应包含线程池管理器、工作线程、任务列队、任务接口等部分。
                        其中线程池管理器的作用是创建、销毁并管理线程池,将工作线程放入线程池中。工作线程是一个可以循环执行任务的线程,在没有任务是进行等待;任务列队的作用是提供一种缓冲机制,将没有处理的任务放在任务列队中;任务接口是每个任务必须实现的接口,主要用来规定任务的入口、任务执行完后的收尾工作、任务的执行状态等,工作线程通过该接口调度任务的执行。
                        线程池管理器至少有下列功能:创建线程池,销毁线程池,添加新任务。
                        工作线程是一个可以循环执行任务的线程,在没有任务时将等待。
                       任务接口是为所有任务提供统一的接口,以便工作线程处理。任务接口主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等。
                
               6). 线程的终止(shutdown、shutdownnow)
                     ExecutorService线程池就提供了shutdown和shutdownNow这样的生命周期方法来关闭线程池自身以及它拥有的所有线程。  
                     shutdown:
                (1)线程池的状态变成SHUTDOWN状态,此时不能再往线程池中添加新的任务,否则会抛出RejectedExecutionException异常。
                         (2)线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出
                            shutdown做了几件事:
                                        1. 检查是否能操作目标线程
                                        2. 将线程池状态转为SHUTDOWN
                                        3. 中断所有空闲线程
                             
                      shutdownNow:
                         (1)线程池的状态立刻变成STOP状态,此时不能再往线程池中添加新的任务。
                         (2)终止等待执行的线程,并返回它们的列表;
                        (3)试图停止所有正在执行的线程,试图终止的方法是调用Thread.interrupt(),但是大家知道,
                                如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。
                                所以,ShutdownNow()并不代表线程池就一定立即就能退出,
                                它可能必须要等待所有正在执行的任务都执行完成了才能退出。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值