多线程

1.什么是线程

一个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程),线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。

ps:进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。

2.线程的串行

一个线程中任务的执行是串行的。如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务。也就是说,在同一时间内,1个线程只能执行1个任务。

3.什么是多线程

一个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务(进程 ->车间,线程->车间工人)。多线程技术可以提高程序的执行效率。

4.多线程的原理

  1.同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)
  2.多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)
  3.如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
  4.如果线程非常非常多,CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源。每条线程被调度执行的频次会降低(线程的执行效率降低)

5.多线程的优缺点

  1.多线程的优点

    1.1能适当提高程序的执行效率

    1.2能适当提高资源利用率(CPU、内存利用率)

  2.多线程的缺点

    2.1开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能

    2.2线程越多,CPU在调度线程上的开销就越大

    2.3程序设计更加复杂:比如线程之间的通信、多线程的数据共享

6.多线程的安全隐患(资源共享)以及解决

  一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源。比如多个线程访问同一个对象、同一个变量、同一个文件,当多个线程访问同一块资源时,很容易引  发数据错乱和数据安全问题。

  如何解决

  使用互斥锁

  @synchronized(锁对象) { 

      // 需要锁定的代码  

  }

  

  互斥锁的优缺点

  优点:能有效防止因多线程抢夺资源造成的数据安全问题

  缺点:需要消耗大量的CPU资源

    互斥锁的使用前提:多条线程抢夺同一块资源 

  相关专业术语:线程同步,多条线程按顺序地执行任务

  互斥锁,就是使用了线程同步技术

7.线程间的通信

  在一个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信

   线程间通信的体现

  一个线程传递数据给另一个线程。/在一个线程中执行完特定任务后,转到另一个线程继续执行任务。

  java中实现方式:

    1.同步(多个线程通过synchronized关键字这种方式来实现线程间的通信。)

      由于线程A和线程B持有同一个MyObject类的对象object,尽管这两个线程需要调用不同的方法,但是它们是同步执行的,比如:线程B需要等待线程A执行完了        methodA()方法之后,它才能执行methodB()方法。这样,线程A和线程B就实现了 通信。这种方式,本质上就是“共享内存”式的通信。多个线程需要访问同一个共      享变量,谁拿到了锁(获得了访问权限),谁就可以执行。

    2.while轮询的方式

      在这种方式下,线程A不断地改变条件,线程ThreadB不停地通过while语句检测这个条件是否成立 ,从而实现了线程间的通信。但是这种方式会浪费CPU资源。之所      以说它浪费资源,是因为JVM调度器将CPU交给线程B执行时,它没做啥“有用”的工作,只是在不断地测试 某个条件是否成立。就类似于现实生活中,某个人一直看着      手机屏幕是否有电话来了,而不是: 在干别的事情,当有电话来时,响铃通知TA电话来了。

    3.wait/notify方式

      当条件未满足时,线程A调用wait() 放弃CPU,并进入阻塞状态。---不像while轮询那样占用CPU

      当条件满足时,线程B调用 notify()通知 线程A,所谓通知线程A,就是唤醒线程A,并让它进入可运行状态。

      这种方式的一个好处就是CPU的利用率提高了。

      但是也有一些缺点:比如,线程B先执行,一下子添加了5个元素并调用了notify()发送了通知,而此时线程A还执行;当线程A执行并调用wait()时,那它永远就不可能      被唤醒了。因为,线程B已经发了通知了,以后不再发通知了。这说明:通知过早,会打乱程序的执行逻辑。

      wait()方法使得当前线程必须要等待,等到另外一个线程调用notify()或者notifyAll()方法。线程调用wait()方法,释放它对锁的拥有权,然后等待另外       的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。

      另一个会导致线程暂停的方法:Thread.sleep(),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的

      notify()方法会唤醒一个等待当前对象的锁的线程。如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关

 

8.线程的状态

  1. 新建状态(New):新创建了一个线程对象。

    Thread t = new Thread ();

       System. out.println(t.getState());//输出NEW

  2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
  3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
  4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    1、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
    2、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
    3、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或           者I/O处理完毕时,线程重新转入就绪状态。
  5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

 

 

 

 

posted @ 2016-10-13 15:31  wxw_wang  阅读(148)  评论(0)    收藏  举报
/* 下雪 begin */ /* 下雪 end */ /* 点击出现爱心特效 begin*/ /* 点击出现爱心特效 end*/