首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

黑马软件工程师_Java多线程基础

2012-12-28 
黑马程序员_Java多线程基础-----------?Android培训、Java培训、Java学习型技术博客、期待与您交流!?--------

黑马程序员_Java多线程基础

-----------?Android培训、Java培训、Java学习型技术博客、期待与您交流!?------------

学习到多线程,就必须对多线程中的概念理解透彻.

程序:

程序是一段静态的代码,它是应用程序执行的蓝本.

进程:

进程是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完整过程,这个过程也是进程本身从产生、发展至消亡的过程.

线程:

线程是比进程更小的单位,一个进程执行过程中可以产生多个线程,每个线程有自身的产生、存在和消亡的过程,也是一个动态的概念.

每个进程都有一段专用的内存区域,而线程间可以共享相同的内存区域(包括代码和数据),并利用这些共享单元来实现数据交换、实时通信与必要的同步操作.

主线程:

每个Java程序都有一个默认的主线程。Java程序总是从主类的main方法开始执行。当JVM加载代码,发现main方法后就启动一个线程,这个线程就称作"主线程",该线程负责执行main方法.

在main方法中再创建的线程就是其他线程。

?

在Java中,创建线程的方式有两种:

1、继承Thread类,覆盖run()方法:使用Thread子类创建线程的优点是可以在子类中增加新的成员变量或方法,使线程具有某种属性或功能。但Java不支持多继承,Thread类的子类不能再扩展其他的类.

2、实现Runnable接口:用Thread类直接创建线程对象,使用构造函数Thread(Runnable target)(参数target是一个Runnable接口),创建线程对象时必须向构造方法参数传递一个实现Runnable接口类的实例,该实例对象称作所创线程的目标对象。当线程调用start()方法,一旦轮到它使用CPU资源,目标对象自动调用接口中的run()方法(接口回调).

线程间可以共享相同的内存单元(包括代码和数据),并利用这些共享单元来实现数据交换、实时通信与必要的同步操作。对于Thread(Runnable target)创建的使用同一目标对象的线程,可以共享该目标对象的成员变量和方法.

另外,创建目标对象类在必要时还可以是某个特定类的子类,因此,使用Runnable接口比使用Thread的子类更具有灵活性.


线程的常用方法:

1.start():线程调用该方法将启动线程,从新建态进入就绪队列,一旦享用CPU资源就可以脱离创建它的线程,独立开始自己的生命周期.

2.run():Thread类的run()方法与Runnable接口中的run()方法功能和作用相同,都用来定义线程对象被调度后所进行的操作,都是系统自动调用而用户不得引用的方法。run()方法执行完毕,线程就成死亡状态,即线程释放了分配给它的内存(死亡态线程不能再调用start()方法)。在线程没有结束run()方法前,不能让线程再调用start()方法,否则将发生IllegalThreadStateException异常.

sleep(int millsecond):有时,优先级高的线程需要优先级低的线程做一些工作来配合它,此时为让优先级高的线程让出CPU资源,使得优先级低的线程有机会运行,可以使用sleep(int millsecond)方法。线程在休眠时被打断,JVM就抛出InterruptedException异常。因此,必须在try-catch语句块中调用sleep方法.

3.?isAlive():当线程调用start()方法并占有CPU资源后该线程的run()方法开始运行,在run()方法没有结束之前调用isAlive()返回true,当线程处于新建态或死亡态时调用isAlive()返回false.

4.?currentThread():是Thread类的类方法,可以用类名调用,返回当前正在使用CPU资源的线程。

5.interrupt():当线程调用sleep()方法处于休眠状态,一个占有CPU资源的线程可以让休眠的线程调用interrupt()方法"吵醒"自己,即导致线程发生IllegalThreadStateException异常,从而结束休眠,重新排队等待CPU资源。

?

线程的几种状态:

线程在一定条件下,状态会发生变化。线程变化的状态转换图如下:

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

  2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

  3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

  4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

  (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

  (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

  (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

  5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

?

创建线程的具体方式:

1.继承Thread类,覆盖run()方法

大致结构是:

?

class 类名 extends Thread{  属性;  …  public void run(){ ..... } //必须覆写run()方法方法; …     }  

?

简单的示例:

?

  public class ThreadDemo { public static void main(String[] args) { Demo demo = new Demo();  demo.start();  for(int i = 1; i <= 100; i++) { System.out.println("main Run(主)" + i); } } }  class Demo extends Thread { public void run() { for(int i = 1; i <= 100; i++) { System.out.println("Demo Run(副)" + i); } } } 
?

运行结果是main与Demo方法的交替出现..

由于循环次数较多,只截取其中的部分片段

?

...................Demo Run(副)93 Demo Run(副)94 Demo Run(副)95 Demo Run(副)96 Demo Run(副)97 Demo Run(副)98 Demo Run(副)99 Demo Run(副)100 main Run(主)48 main Run(主)49 main Run(主)50 main Run(主)51 main Run(主)52 main Run(主)53 main Run(主)54...................
?

每次运行结果都不相同:因为多个线程获取cpu的执行权,cpu执行到谁,谁就运行.多线程一个特性:随机性.

?

如果将上述代码中的demo.start()换成demo.run().此时就类似于单线程了,先执行完Demo Run(副),然后再执行main Run(主).

结果分析:

如果是直接调用run()方法,此时并没有使用新建的线程执行,还在主线程的内部进行执行,而调用start()表明开启新的进程,并在新的进程中执行该段方法.

直接调用run()方法相当于在主方法中对象直接调用本类方法.

2.实现Runnable接口

具体步骤:

?

--定义类实现Runnable接口

--覆盖Runnable接口中的run方法,将线程要有运行的代码放在run方法中

--通过Thread类建立线程对象

--将Runnable接口的子类对象作为实际对象传给Thread类的构造函数,自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去明确指定对象的run方法

--调用Thread类的start方法开启线程.

大致结构是:

?

class 类名 implements Runnable{  属性; …  public void run(){ ..... } //实现接口中run()方法 方法;    .... }  

??简单的示例:

?

 class TicketRun implements Runnable { private int ticket = 100;  public void run() { while (true) { if (ticket > 0) { System.out.println(Thread.currentThread().getName() + " Sale : " + ticket--); } } } }  public class TicketRunDemo { public static void main(String[] args) { TicketRun rd = new TicketRun();  Thread t1 = new Thread(rd);// 创建线程对象时,就要明确需要运行的run方法 Thread t2 = new Thread(rd);   t1.start(); t2.start();   } } 

?

运行结果(部分截取)

?

.........................Thread-3 Sale : 46 Thread-1 Sale : 47 Thread-3 Sale : 42 Thread-0 Sale : 43 Thread-2 Sale : 44 Thread-0 Sale : 39 Thread-3 Sale : 40 Thread-1 Sale : 41 Thread-3 Sale : 36 Thread-3 Sale : 34 Thread-0 Sale : 37 Thread-2 Sale : 38 Thread-0 Sale : 32 Thread-3 Sale : 33.............................
?

那么两者有什么区别?

两者线程代码存放的位置不同:实现接口的线程代码放在接口的子类run方法中,继承方式的线程代码存放在Thread子类的run方法中

实现接口方式避免了单继承的局限性,在定义线程时,建议实现接口方式.

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

?

实现Runnable接口比继承Thread类所具有的优势:

while (true) { synchronized (this) { if (ticket > 0) { try { Thread.sleep(10); } catch (Exception e) { System.out.println("error"); } System.out.println(Thread.currentThread().getName() + " --code-- : " + ticket--); } }}?

【同步方法】

public synchronized void show(){ if (ticket > 0) { try { Thread.sleep(10); } catch (Exception e) { System.out.println("error"); } System.out.println(Thread.currentThread().getName() + " ----show---- : " + ticket--); } }?

需要注意的问题:

public class ThisLockDemo { public static void main(String[] args) { TicketRun rd = new TicketRun(); Thread t1 = new Thread(rd);// 创建线程对象时,就要明确需要运行的run方法 Thread t2 = new Thread(rd); t1.start(); try { Thread.sleep(20); } catch (Exception e) { } rd.flag = false; t2.start(); } } class TicketRun implements Runnable { private int ticket = 100; boolean flag = true; //Object o = new Object(); public void run() { if (flag) { while (true) { synchronized (this) { if (ticket > 0) { try { Thread.sleep(10); } catch (Exception e) { System.out.println("error"); } System.out.println(Thread.currentThread().getName() + " --code-- : " + ticket--); } } } } else while (true) show(); } public synchronized void show()//若同步方法被static修饰.锁是Class对象,则使用的锁是类名.class { if (ticket > 0) { try { Thread.sleep(10); } catch (Exception e) { System.out.println("error"); } System.out.println(Thread.currentThread().getName() + " ----show---- : " + ticket--); } } }?

?

?

小插曲:单例设计模式

?

饿汉式与懒汉式

饿汉式:

?

class Single { private static final Single s = new Single();  private Single(){}  public static Single getInstance() { return s; } }
?

懒汉式:(实例的延时加载)--->保证不了单例,存在安全问题.

?

class Single { private static Single s = null;  private Single(){}  public static Single getInstance()  { if(s == null) s = new Single(); return s; } }

?

加强的懒汉式:

?

public class Single { private static Single s = null; private Single() {}  public static Single getInstance() { if(s == null) { synchronized(Single.class) { if(s == null) s = new Single(); } } return null; } }

?

?

Done...

?

?

?

?

热点排行