java 多线程基础(一)——线程创建及五种状态

来源:互联网 发布:淘宝找报销发票关键词 编辑:程序博客网 时间:2024/05/16 16:55

    最近在使用SOA的模式重构高校平台,在考试系统模块,期望使用上多线程,之前对多线程有过一些了解,不过具体的实现是在.net平台下的。虽然一年前接触过j2se的关于多线程的一些知识,但是感觉还是不够,于是最近,也算是在项目的驱动下,抽时间继续学习了一些java的多线程,这里拿出来分享一下。

一、多线程基础知识:

    对于多线程的基础知识,这里不做赘述,只是简单的交代两句。

  •     对于单核的cpu来说,多线程程序,本质也是单线程的,只是cpu在不停的切换。
  •     线程的优先级代表的是相关线程抢占cpu的频率。
  •    多线程编程不同于多核编程(多核编程控制cpu运行)。
  •    线程运行在进程中,可以看做是进程的一些个小单元。
二、关于线程的重点:
  • 线程的创建
  •  线程的五种状态
  •  线程同步
  •  线程间的通信
  •  生产者消费者问题
  •  jdk5.0以后对线程的优化
三、创建线程的两种方法:
  •  继承Thread类
/** * 方法一: * 继承Thread类创建线程 * @author LiMin * */public class CreateThread extends Thread {private String name;public CreateThread(){}public CreateThread(String name){/*this.name=name;*///调用父类的给线程命名的方法super(name);}public void run(){for(int x =0;x<60;x++){/*System.out.println(this.name+ " run -----"+ x);*//*System.out.println(this.getName()+ " run -----"+ x);*///使用Thread的静态方法获取线程名称Thread.currentThread().getName()是标准通用方式 /*System.out.println(Thread.currentThread().getName()+ " run -----"+ x);*/System.out.println((Thread.currentThread()==this)+"....."+this.getName()+ " run -----"+ x);}}}

调用:
public class ThreadTest {/** * @param args */public static void main(String[] args) {CreateThread t1 = new CreateThread("one----");CreateThread t2= new CreateThread("two+++++");t1.start();t2.start();for(int x =0;x<60;x++){System.out.println("main-----"+ x);}}}

    程序比较简单,不多解释,重点注意:
  • 虚拟机定义主线程的main方法,Thread类的run方法,只是一个代码存储区。
  • start是底层的一个方法,调用了相应线程的run方法
  • run方法,并不是多线程的,只是一个对象的调用,主线程会等待这个对象的方法执行完后,再继续。
  • 系统会为每一个线程对象创建一个内存空间,所以,这里的for循环中的x都是一个独立的,不同的线程是互不影响的。
  • 除了售票以外,jvm还要调用dos的打印窗口,因为是多核的cpu 2号票已经出来了,但是还没有打印,这时候1号票也已经卖出了,结果先打印的是1号票,后打印1号票。



可以使用下面的程序验证run和start方法的区别
public class ThreadDemo {public static void main(String[] args){CreateThread d = new CreateThread();//开启线程,并执行该线程的run方法d.start();//仅仅是对象调用方法。而线程创建了,并没有运行。/*d.run();*/ for(int x =0;x<60;x++){System.out.println("Hello word !-----"+ x);}}}


  • 实现Runnable接口 (多个线程卖票

创建线程:
package TicketRunnable;public class Ticket2 implements Runnable{ //extends Thread{//如果是静态的话,所有的线程会共享这100张票/*private  static int  ticket =100;*///如果不是静态的话,每一个线程对象都会创建这个ticket对象,也即每个线程拥有100张票卖。//这时候,如果还是采用继承Thread类的方法就不行了,需要使用实现Runnable接口创建线程private  int  ticket =1000;Object obj= new Object();public void run() {//在这里声明的对象是无效的/*Object obj= new Object();*/while(true){synchronized (obj) {if(ticket>0){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+".....sale : "+ ticket--);}}}} }

调用:
package TicketRunnable;public class TicketDemo2 {/** * @param args */public static void main(String[] args) {Ticket2 t = new Ticket2();Thread t1 = new Thread(t);Thread t2 = new Thread(t);Thread t3 = new Thread(t);Thread t4 = new Thread(t);t1.start();t2.start();t3.start();t4.start();}}

重点注意:

  • 将多线程要运行的代码存放在run方法中。
  • 通过Thread类建立线程对象。
  • 在创建线程对象的时候,就指定run方法所属的对象。

    将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数,这是因为:自定义的run方法所属的对象是Runnable接口的子类对象,所以,要让线程去指定对象的run方法,就必须明确run方法所属对象。

  • 调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。


    对比:
    java是单类继承,使用Thread类继承,有很大的局限。使用Runnable,既不影响,你要开发的类的继承关系,同时也可以满足多线程的扩展,所以,提倡使用接口实现的方式。


四、线程的五种状态:
    一个线程可以有:创建、运行、临时阻塞、冻结(又分为睡眠和等待两种状态)、消亡五种状态。认识这五种状态是认识多线程的基础。看下图:

冻结:又分为睡眠和等待两种状态。

调用了start方法后,还在等待cpu来处理启动,这个时候的线程处于临时状态。

对应的一些常见的方法汇总成一个表格:

线程方法名称

是否释放同步锁

是否需要在同步的代码块中调用

方法是否已废弃

是否可以被中断

sleep()

wait()

suspend

 

 

 

resume()

 

 

 

join()

 

 



总结:
    这里使用具体的实例介绍了两种创建线程的方法,并给出了两种策略的优劣,最后得出,一般使用实现Runnable接口的方法。
    调用线程的不同方法会在线程的五种状态:创建、运行、临时阻塞、冻结之间切换。了解线程的方法和状态,是线程同步(锁:代码块、函数)以及线程间通信的基础。
    下一篇博客会介绍几种线程同步的策略以及相关的锁。

0 0
原创粉丝点击