多线程总结

来源:互联网 发布:龙信数据怎么样 编辑:程序博客网 时间:2024/06/09 19:38

多线程总结

一、提到多线程,就不得不理解以下几点:

1.程序,进程,线程这三者之间的关系?

三者的逻辑关系是程序调用进程,进程调用线程,一般来说程序下面都是多进程,不同的进程分工不同;进程下面也基本上是多线程的。

可以这样下定义:进程是系统进行资源分配和调用的独立单位,每一个进程,都由它自己的内存空间和系统资源

线程是进程的执行单元,执行路径,线程也是程序使用CPU的最基本单位

2.单线程与多线程以及多线程的意义?

多线程应用范围:主要是大型项目的开发

优点:将大流程,拆分若干个小流程,条理清晰,便于阅读!多线程一般是异步结构的,执行效率高!

 

如果程序只有一条执行路径,这就是单线程;相反如果程序有多条路径,那么就是多线程

看下面这段程序:

public class Thread {

public static void main(String[]args) {

//code..........

............

...........

}

}

这里面至少存在两条程序,一条是主线程main,另外一条就是垃圾回收线程,即JVM一遍"执行"main"一边"也在执行垃圾回收机制

所以说Java支持多线程,而多线程的意义它可以让程序在一个时间段执行多个事情,提高了应用程序的使用率;

比如上面那个例子,如果是单线程,那么JVM不得不执行一会main线程然后停下来去执行垃圾回收机制

3.理解并发与并行

思考:我们在一边玩游戏,一边带上耳机听歌,请问玩游戏与听歌是同时进行的吗?

显然不是,因为CPU在某一个时间点上只能做一件事,我们玩游戏,CPU就切换到执行游戏进程,听歌,CPU就切换到听歌进程;CPU就这样反复做这高效率的切换动作,这种切换是随机的,但是让我们人感觉到就是同时发生的,这就是并发的概念

并发:通过CPU调度算法,让用户看上去同时执行,实际上,是通过CPU在高速切换,并不是真正的额同时

并行:多个CPU实例或者多台机器同时执行一段处理逻辑,这就是真正的同时;

二,如何创建线程?

这里先提两个:run()方法,与start()方法,run()方法里面存放着Thread线程对象需要执行的代码;调用start()方法,让所创建的线程处于就绪状态。

创建线程的方式一:

1.类去继承(extends)Thread

2.该类重写Thread类的run方法,并且将线程要执行的代码,存放到run方法中

3.线程对象调用start方法,开启线程,线程会自动执行run方法

创建线程的方式二:

1.存放线程执行代码的类去实现(implements) Runnable接口

2.重写所实现接口的run方法,并将线程执行代码存放在run方法中

3.创建Thread对象,也就是创建线程

4.Thread线程对象调用start方法,启动线程

 

总结:

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

 

三、线程在程序中的几种状态

线程调度

线程有5种基本状态:



1、出生:新建状态Thread t = newThread),当创建线程对象后,就进入了新建状态。

2、就绪:就绪状态runnable),t.start()被调用;当线程对象被创建后,其他线程调用了该线程的start()方法,该线程就进入了队列,说明此线程做好了准备,随时等待cpu调度执行。

3、获得CPU执行权:运行状态running)当cpu时间片分给就绪状态的线程时,该线程就真正的执行起来,进入了运行状态。

4阻塞状态blocked)阻塞状态是因为线程因为某种原因放弃了cpu的使用权,暂时停止运行,直到线程进入就绪状态,才有机会运行。

阻塞状态分为3种情况:

执行-等待阻塞(t.wait()):运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;自己醒不了,只能用notify()或者notifyAll()唤醒,处于等待状态的线程会释放CPU执行权,同时释放锁资源。

同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

其他阻塞 -- 通过调用线程的sleep()join()发出了I/O求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5死亡状态Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

处于阻塞状态的线程往往存在性能瓶颈,也是我们需要特别关注的。如果是因为竞争同步锁引发的死锁或者的响应时间的延长,需要找到这些处于blocked状态的线程在等待什么资源,通常情况下是数据库连接或者是日志的输出。

四、多线程的安全隐患以及synchronized监视器()

1.多线程并发安全问题

 *由于线程之间运行是异步的,互相没有牵制,但如果

 *同时访问同一资源时就出现了的现象。由于线程

 *发生切换现象的实际不确定导致代码执行顺序可能未

 *按照设计的顺序执行,出现一系列的不可预知后果

public class SyncDemo{

    public staticvoid main(String[] args) {

        finalTable table = new Table();

        Thread t1= new Thread(){

            publicvoid run(){

                while(true){

                   intbean = table.getBean();

                   Thread.yield();//模拟线程发生了切换

                   System.out.println(

                       getName()+":"+bean);

                }

            }

        };

        Thread t2= new Thread(){

            publicvoid run(){

                while(true){

                   intbean = table.getBean();

                   Thread.yield();//模拟线程发生了切换

                    System.out.println(getName()+":"+bean);

                }

            }

        };

        t1.start();

        t2.start();

    }

}

class Table{

    private intbeans = 20;

    /**

     * 当一个方法被synchronized修饰后,该方法称为

     * 同步方法,即:多个线程不能同时进入到方法内部

     * 去执行代码。这样就不会同时对beans这个数据

     * 进行操作,没有抢的问题,就不会出现并发安全

     * 问题了。

     *

     * 在一个方法上使用synchronized修饰,那么

     * 同步监视器对象就是该方法所属对象,即方法

     * 内部看到的this

     * @return

     */

    publicsynchronized int getBean(){

        if(beans==0){

            thrownew RuntimeException("没有豆子了!");

        }

        Thread.yield();//模拟线程发生了切换

        returnbeans--;

    }

}

2.同步代码块可以解决线程安全问题

三、线程池

JDK1.6中的内置线程池——Executor框架

       JDK中提供了一套Executor框架支持更好的控制多线程操作。在concurrent包中是并发线程的核心,其中ThreadPoolExecutor表示一个线程池,Executors扮演者线程池工厂的角色,通过Executors可以创建特定功能的线程池。


  1. //1、new一个固定线程数量的线程池,有一个新任务提交时,从该线程池中查找是否有空闲线程,如果没有新的任务会被暂存于一个任务队列中,待有空闲线程后再执行。  
  2. ExecutorService threadPoolFix=Executors.newFixedThreadPool(3);//每次3个线程,执行3个任务;其他任务等待。     
  3. //2、缓存线程池--返回一个跟任务数相等的线程数量,有多少任务就产生多少个线程进行处理。动态变化-干完了定期回收  
  4. ExecutorService threadPoolCache=Executors.newCachedThreadPool();  
  5.   
  6. //3、只有一个线程的线程池,任务按照任务队列顺序FIFO执行,保证线程池始终中有一个线程,当前死了,立马搞一个顶替工作  
  7. ExecutorService threadOne=Executors.newSingleThreadExecutor();  
  8.   
  9. //4、线程池大小为1,支持在给定时间执行某任务,如在某个固定的延时之后执行,或者周期性执行。-定时器效果  
  10. ExecutorService threadSingleScheduled=Executors.newSingleThreadScheduledExecutor();  
  11.   
  12. //5、指定一定固定大小的线程池  
  13. ExecutorService threadScheduled=Executors.newScheduledThreadPool(5);