深入浅出多线程
线程
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对 运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。Java在语言层面对多线程提供了卓越的支 持,它也是一个很好的卖点。
线程和进程的区别
线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。
用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现..因为反应”多角色”的程序代码,最起码每个角色要给他一个线程,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的”生产者.消费者模型”.
常见的概念的数据字典:
多线程: 指在这个程序(一个进程)运行时产生了不止一个线程.
并行与并发:
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时
并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时.并发往往在场景中有公用的资源,针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力.
并发与并行:
线程安全:经常用来描述一段代码.指在并发的情况下,该代码经过多线程使用,线程的调度顺序不影响任何结果.这个时候使用多线程,只需要关注系统的内存,cpu是不是够用即可.反过来,线程不安全就意味着线程的调度顺序会影响最终结果.如不加事务的转账业务;
同步:java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确.对无事务的代码添加关键字@synchronized.在保证结果准确的同时,提高性能,才是优秀的程序.线程安全的优先级高于性能.
各种状态一目了然,值得一提的是”blocked”这个状态:
线程在Running的过程中可能会遇到阻塞(blocked)情况.
- 调用join()和sleep()方法,sleep()时间结束或被打断,join()中断.IO完成都会回到Runnable状态,等待jvm的调度.
- 调用wait()方法,使该线程处于等待池(wait blocked pool),知道notify()/notifyAll(),线程被唤醒被放到索定池(lock blocked pool),释放同步锁使线程回到可运行状态(Runnable)
- 对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool),同步锁被释放进入可运行状态(Runnable).
synchronized,wait,notify是任何对象都具有的同步工具.
synchronized,wait,notify是应用于同步问题的人工线程调度工具.讲其本质,首先就要明确monitor的概念,java中的每个对象都有一个监视器,来检测并发代码的重入.在非多线程编码时该监视器不发挥作用,反之如果在synchronized范围内,监视器发挥作用.
wait/notify必须存在于sychronized块中.并且,这三个关键字针对的是同一个监视器(某对象的监视器).这意味着wait之后,其他线程
可以进入同步块执行.
当某代码并不持有监视器的使用权时(如图5的状态,即脱离同步块)去wait或notify,会抛出java.lang.IllegalMonitorStateException.也包括在synchronized.也包括在synchronized块中去调用另一个对象的wait/notify,因为不同对象的监视器不同,同样会抛出此异常.
线程和进程一共分为5个阶段:
创建,就绪,运行,阻塞,终止.
实现多线程的三种方式:
- 继承Thread类
- 实现Runnable接口.
- 实现Callable接口,并与Future.线程池结合使用.
Thread类和Runnable接口的区别
如果一个类继承Thread类,则不适合资源共享,但是如果实现了Runnable接口的话,则很容易的实现资源共享.
- 实现Runnable接口,适合多个相同的程序代码的线程去处理同一个资源;
- 实现接口可以避免java中的单继承的限制
- 实现接口可以增加程序的健壮性,代码可以被多个线程共享,代码和数据独立.
- 线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类.
多线程常见线程名词解释:
主线程:JVM调用main()所产生的进程
当前线程:这个是容易混淆的概念.一般指通过Thread.currentThread()来获取的进程.
后台线程:指为其他线程提供服务的线程,也称为守护线程.JVM的垃圾回收线程就是一个后台线程.用户线程和守护线程的区别在于,是否等待主线程依赖主线程结束而结束.
前台线程:是指接收后台线程服务的线程,其实前台后台线程是联系在一起.可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程.
线程类的常用API:
sleep():强迫一个线程睡眠N毫秒;
isAlive():判断一个线程是否存活;
join()等待线程终止;
activeCount():程序中活跃的线程数;
enumerate()枚举程序中的线程.
currentThread()得到当前线程;
isDaemon();一个线程是否为守护线程;
setDaemon();设置一个线程为守护线程.(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
setName();为线程设置一个名称;
wait();强迫一个线程等待;
notify():通知一个线程继续运行;
setPriority()设置一个线程的优先级;
synchronized关键字的作用域
- 是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其他线程不能同时访问这个对象中任何一个synchronized方法).这时,不同的对象实例的synchronized方法是不相干扰的.也就是说,其他线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
- 是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static方法.他可以对类的所有对象实例起作用.
除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中.表示只对这个区块的资源实行互斥访问.用法是synchronized(this){},他的作用域是当前对象.
synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){}在继承类中并不自动是synchronized f(){},而是变成了f(){}.继承类需要你显示的指定他的某个方法为synchronized方法;
还没有评论,来说两句吧...