深入浅出多线程

线程

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对 运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。Java在语言层面对多线程提供了卓越的支 持,它也是一个很好的卖点。

线程和进程的区别

线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。

 

用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现..因为反应"多角色"的程序代码,最起码每个角色要给他一个线程,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的"生产者.消费者模型".

常见的概念的数据字典:

多线程: 指在这个程序(一个进程)运行时产生了不止一个线程.

并行与并发:

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

       并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时.并发往往在场景中有公用的资源,针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力.

 

并发与并行:

   线程安全:经常用来描述一段代码.指在并发的情况下,该代码经过多线程使用,线程的调度顺序不影响任何结果.这个时候使用多线程,只需要关注系统的内存,cpu是不是够用即可.反过来,线程不安全就意味着线程的调度顺序会影响最终结果.如不加事务的转账业务;

    同步:java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确.对无事务的代码添加关键字@synchronized.在保证结果准确的同时,提高性能,才是优秀的程序.线程安全的优先级高于性能.

各种状态一目了然,值得一提的是"blocked"这个状态:

线程在Running的过程中可能会遇到阻塞(blocked)情况.

  1. 调用join()和sleep()方法,sleep()时间结束或被打断,join()中断.IO完成都会回到Runnable状态,等待jvm的调度.
  2. 调用wait()方法,使该线程处于等待池(wait blocked pool),知道notify()/notifyAll(),线程被唤醒被放到索定池(lock blocked pool),释放同步锁使线程回到可运行状态(Runnable)
  3. 对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个阶段:

   创建,就绪,运行,阻塞,终止.

实现多线程的三种方式:

  1. 继承Thread类
  2. 实现Runnable接口.
  3. 实现Callable接口,并与Future.线程池结合使用.

Thread类和Runnable接口的区别

如果一个类继承Thread类,则不适合资源共享,但是如果实现了Runnable接口的话,则很容易的实现资源共享.

  1. 实现Runnable接口,适合多个相同的程序代码的线程去处理同一个资源;
  2. 实现接口可以避免java中的单继承的限制
  3. 实现接口可以增加程序的健壮性,代码可以被多个线程共享,代码和数据独立.
  4. 线程池只能放入实现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关键字的作用域

  1. 是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其他线程不能同时访问这个对象中任何一个synchronized方法).这时,不同的对象实例的synchronized方法是不相干扰的.也就是说,其他线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
  2. 是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static方法.他可以对类的所有对象实例起作用.

除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中.表示只对这个区块的资源实行互斥访问.用法是synchronized(this){},他的作用域是当前对象.

 

synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){}在继承类中并不自动是synchronized f(){},而是变成了f(){}.继承类需要你显示的指定他的某个方法为synchronized方法;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值