黑马程序员_java学习笔记 6.多线程

来源:互联网 发布:计算机技术与软件 证书 编辑:程序博客网 时间:2024/05/29 10:08

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


多线程


一、什么是多线程


1、线程的定义

   进程:是一个正在执行的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。

   线程:就是进程中的一个独立的控制单元, 线程在控制着进程的执行,一个进程中至少有一个线程。该进程中至少有一个线程,负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。

2、多线程的分类
   用户线程:用户产生的线程
   精灵线程:为用户线程提供服务的线程,通常在后台运行。
Java虚拟机至少有一个精灵线程,即垃圾收集线程。垃圾收集线程只在系统空闲时运行,它是一个低优先级线程。

Java VM 启动时会有一个进程java.exe

扩展:其实更细节说明jvm,jvm启动的不止一个线程,还有垃圾回收机制的线程。


3、多线程的优势

  减轻编写交互频繁、涉及面多的程序的困难


二、多线程的实现方式


1 声明一个 Thread 类的子类,并覆盖 run() 方法。

[java] view plaincopy
  1. class Mythread extends Thread  
  2. {  
  3.     public void run( )   
  4.     {   
  5.     }  
  6. }  

2 声明一个实现 Runnable 接口的类,并实现 run() 方法

[java] view plaincopy
  1. class Mythread implements Runnable  
  2. {  
  3.     public void run( )   
  4.     {   
  5.     }  
  6. }  



3、两种方式的区别

  区别一:
  由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
  构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable是否为空, 不为空则执行Runnable的run()
  区别二:
  继承Thread只能是单继承,如果自己定义的线程类已经有了父类,就不能再继承了
  实现Runnable接口可以多实现,即使自己定义线程类已经有父类可以实现Runnable接口

 

4.优缺点

继承Thread的好处是:可以直接使用Thread类中的方法,代码简单。 弊端是:如果已经有了父类,就不能用这种方法

实现Runnable接口的好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口。而且接口是可以多实现的。 弊端是:不能直接使用Thread中的方法。需要先获取到线程对象后,才能得到Thread的方法,代码复杂



三、多线程的安全问题


1、 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,
    另一个线程参与进来执行。导致了共享数据的错误。

    解决办法:
    对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中其他线程不可以参与执行。


2、

Java对于多线程的安全问题提供了专业的解决方式,
就是同步代码块。

synchronized(对象)
{
   需要被同步的代码。
}
对象如同锁,持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

3、

同步的前提:
1、必须要有两个或两个以上的线程。
2、必须是多个线程使用同一个锁。

必须保证同步中只能有一个线程在运行。

好处:解决了多线程的安全问题。

弊端:多个线程需要判断锁,较为消耗资源。

 

同步函数用的是哪一个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this。
所以同步函数使用的锁是this。

 

如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不再是this。因为静态方法中也不可以定义this。

静态进入内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
类名.class  该对象的类型是Class

静态的同步方法,使用的锁是该方法所在类的字节码文件对象。也就是 类名.class

 

4、死锁

所谓死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象:死锁。”



四、设计模式


单例设计模式之饿汉式和懒汉式


1、饿汉式:


[java] view plaincopy
  1. <pre name="code" class="java">class Single  
  2. {  
  3.     //私有化对象  
  4.     private static final Single s=new Single();       
  5.     private Single(){}  
  6.     //提供一个获取对象的方法  
  7.     public static Single getInstance()  
  8.     {  
  9.         return s;  
  10.     }  
  11. }  

2、懒汉式:

[java] view plaincopy
  1. <pre name="code" class="java">class Single  
  2. {  
  3.     //定义一个空对象  
  4.     private static Single s=null;  
  5.     private Single(){}  
  6.     //定义get方法  
  7.     public static Single getInstance()  
  8.     {  
  9.         //定义判断条件  
  10.         if(s==null)  
  11.         {  
  12.             //判断锁  
  13.             synchronized(Single.class)  
  14.             {  
  15.                 if(s==null)  
  16.                     s=new Single();//创建对象  
  17.             }  
  18.         }  
  19.         return s;//返回对象  
  20.     }  
  21. }  



懒汉式特点是延迟加载,但是多线程访问时会出现安全问题,这时加锁可以解决安全问题,但是会让程序低效。用双重判断的方式,可以解决效率问题

五、线程之间的通信


 1、 如果希望线程等待,就调用wait()
       如果希望唤醒等待的线程,就调用notify();
       这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用


2、多个线程通信的问题

  notify()方法是随机唤醒一个线程(优先唤醒先等待的线程)
  notifyAll()方法是唤醒所有线程 

  JDK1.5之前无法唤醒指定的一个线程
  如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件

 

1.5中提供了多线程升级解决方案:

将同步Synchronized替换成现实Lock操作,

将object中wait,notify,notifyAll替换成了Condition对象

该对象可以通过Lock锁进行获取  lock.newCondition()


3、停止线程:

      run方法结束,只有控制循环,就可以让run方法结束。



0 0
原创粉丝点击