黑马程序员_多线程技术_(线程的创建 Thread和Runnable、死锁、线程间通信、join、yield)

来源:互联网 发布:独战乾坤伙伴进阶数据 编辑:程序博客网 时间:2024/05/22 02:10

-------android培训、java培训、期待与您交流! ----------

1.多线程概述

(1)进程:正在执行中的程序,比如QQ,javac等

(2)线程:进程中的一个独立控制单元,线程控制着进程的执行。一个进程至少有一个线

(3)多线程的好处:可使程序产生同时运行的效果,多线程下载可提高效率。

2.多线程的两种创建方式:

第一种方式:继承Thread类

创建步骤:

(1)定义一个类继承Thread(那么此类也就变成了Thread类,拥有Start方法。

(2)复写Thread类中的run方法(将需要使用多线程运行的代码存在run方法,让线程运行)

(3)调用线程的start方法.

注意:start():开启一个新线程并运行run方法中的代码,

而调用run方法只是对象调用方法,而不会创建新的线程。


第二种方式:实现Runnable接口。

创建步骤:

(1) 定义类实现Runnable接口

(2)覆盖Runnable接口中的run方法。(还没有拥有线程)

(3)通过Thread创建线程对象。

(4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

(5)调用Thread类的Start方法开启线程,Runnable接口的run方法即可运行。

3.实现方式和继承方式的区别

(1)继承了一个类不能再继承其他类,java只支持单继承,这样就会出现类继承的局限性。

(2)继承Thread:线程代码存放在Thread类的run方法中,直接就可以调用子类的Start方法即可开启线程。

(3)实现Runnable:线程代码存放在Runnable接口的子类的run方法中,开启线程还需要new Thread();

(4)在定义线程时,建议使用实现方式。

4.线程的状态和获取线程对象以及名称

线程的状态: 1被创建——2运行——3阻塞(临时状态,具备执行资格但没执行)——4冻结———5消亡。

获取都有自己的名称:Thread-编号从0开始

获取对象方法:

static Thread currentThread()获取当前线程对象。getName()获取线程名称。setName()设置线程名称。

5.线程的安全解决

(1)产生安全问题的原因:当多个线程在操作同一个共享数据时,

一个线程还没执行完,另一个线程参与进来,导致共享数据产生了错误。

(2)解决办法:

同步代码块: synchronized(锁对象){需要被同步的代码数据;}

<1>对象如同锁,持有锁的线程才可以在同步中执行。

<2>同步的前提:必须要有两个或两个以上的线程,必须是多个线程使用的同一个锁对象。

同步函数:

非静态同步函数的锁:this

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

静态同步函数的锁:class对象

因为静态方法中不可以定义this,所以锁不可能是this。静态进内存时,内存中没有本类对象,

但是一定有该类对应的字节码文件对象,类名.class,该对象的类型是class.

6死锁:同步中钳套同步容易出现死锁现象,应该避免这样现象发生。

[java] view plaincopy
  1. <span style="font-size:14px;">class Test implements Runnable  
  2. {  
  3.     private boolean flag;  
  4.     Test(boolean flag)  
  5.     {  
  6.         this.flag = flag;  
  7.     }  
  8.   
  9.     public void run()  
  10.     {  
  11.         if(flag)  
  12.         {  
  13.             while(true)  
  14.             {  
  15.                 synchronized(MyLock.locka)  
  16.                 {  
  17.                     System.out.println(Thread.currentThread().getName()+"...if locka ");  
  18.                     synchronized(MyLock.lockb)  
  19.                     {  
  20.                         System.out.println(Thread.currentThread().getName()+"..if lockb");                    
  21.                     }  
  22.                 }  
  23.             }  
  24.         }  
  25.         else  
  26.         {  
  27.             while(true)  
  28.             {  
  29.                 synchronized(MyLock.lockb)  
  30.                 {  
  31.                     System.out.println(Thread.currentThread().getName()+"..else lockb");  
  32.                     synchronized(MyLock.locka)  
  33.                     {  
  34.                         System.out.println(Thread.currentThread().getName()+".....else locka");  
  35.                     }  
  36.                 }  
  37.             }  
  38.         }  
  39.     }  
  40. }  
  41.   
  42.   
  43. class MyLock  
  44. {  
  45.     static Object locka = new Object();  
  46.     static Object lockb = new Object();  
  47. }  
  48.   
  49. class  DeadLockTest  
  50. {  
  51.     public static void main(String[] args)   
  52.     {  
  53.         Thread t1 = new Thread(new Test(true));  
  54.         Thread t2 = new Thread(new Test(false));  
  55.         t1.start();  
  56.         t2.start();  
  57.     }  
  58. }</span>  


7.线程间通信

(1)线程间通信:

其实就是多个线程在操作同一个资源,但操作的动作不同。

(2)等待唤醒机制:

<1>wait(),notify(),notifyAll()都使用在同步中,因为只有同步才具有锁,所以要对持有锁的线程操作。

<2>这些用来操作线程的方法为什么定义在Object类中?

a.这些方法存在于同步中。

b.使用这些方法时必须要标识所属的同步的锁。

c.等待和唤醒必须是同一个锁。

d.锁可以是任意对象,所以任意对象调用的方法一定定义在Object中。

(3)wait(),sleep()的区别:

wait():释放资源,释放锁。

sleep():释放资源,不释放锁。

(3)举例:生产者与消费者

<1>对于多个生产者和消费者,为让被唤醒的线程再一次判断标记,故要定义while判断标记。

<2>为什么定义notifyAll?

因为需要唤醒对方线程,只用notify容易出现只唤醒本方线程的情况,导致程序中的所有

线程都等待。

<3>JDK1.5提供的多线程升级解决方案。

将同步synchronized替换成现实Lock操作。将Object中的wait,notify,notifyAll替换成了

condition对象。该对象可以用Lock锁进行获取。

8.停止线程:

(1)stop方法已经过时。

(2)如何停止线程?

只有一种,就是让run方法结束,开启多线程运行,运行代码通常是循环结构。只要控制住循环,

就可以让run方法结束,也就是线程结束。

(3)特殊情况:

<1>当线程处于了冻结状态,就不会读到标记,那么线程就不会结束。

<2>当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复

到运行状态中来,这样就可以操作标记让线程结束。

(4)Thread类提供该方法:interrupt()

9.守护线程:

setDaemon:

public final void setDaemon(boolean on)

(1)当正在运行的线程都是守护线程时,JVM退出。

(2)该方法必须在启动线程前调用。

(3)主线程是前台线程,前台线程一结束,守护线程自动结束。

10.join方法:

join://抢夺CPU执行权

public final void join()

throws InterruptedException

等待该线程终止

(1)当A线程执行到了B线程的join()方法时,A就会等待,等B线程都执行完,A才会执行。

(2)join可以用来临时加入线程执行。

11.优先级&yield方法

setPriority(int newPriority)

更改线程的优先级(默认是5)

(1) MAX_PRIORITY-->10

NORM_PRIORITY--->5

MIN_PRIORITY--->1

(2)注:在任务管理器的进程中,也可进行优先级设置。

yield:临时停止

public static void yield()

暂停当前正在执行的线程对象,并执行其他线程。yield可稍微减缓线程的运行,

使线程达到接近平均运行的效果。