java 多线程简述(一)

2014-11-24 02:35:59 · 作者: · 浏览: 2

多线程的基本实现

进程指运行中的程序,每个进程都会分配一个内存空间,一个进程中存在多个线程,启动一个JAVA虚拟机,就是打开个一个进程,一个进程有多个线程,当多个线程同时进行,就叫并发。

J ava创建线程的两种方式为: 继承Thread类 和实现Runnable接口

Thread类

1、通过覆盖run方法实现线程要执行的程序代码

2、Start()开始执行多线程

package com.bin.duoxiancheng;
public class d1 extends Thread{
    public void run(){
       for(int i=0 ; i<50; i++){
           System.out.println(i);
           System.out.println(currentThread().getName());
           try {
              sleep(100);
           } catch (InterruptedException e) {
              // TODO Auto-generatedcatch block
              e.printStackTrace();
           }
       }
    }
    public static void main(String[] args){
       new d1().start();
       new d1().start();
    }
}


多个线程共享一个实例的时候,代码代码如下:

package com.bin.duoxiancheng;
public class d1 extends Thread{
    int i=0;
    public void run(){
       for(i=0 ; i<50; i++){
           System.out.println(i);
           System.out.println(currentThread().getName());
           try {
              sleep(100);
           } catch (InterruptedException e) {
              // TODO Auto-generatedcatch block
              e.printStackTrace();
           }
       }
    }
    public static void main(String[] args){
       new d1().start();
       new d1().start();
    }
}


结果如下所示:

0

Thread-1

0

Thread-0

1

Thread-1

1

实际2个线程在操纵不同的变量a,在执行run方法时候,线程把a都当做自己的变量在执行。

Runnable接口实现多线程

当一个继承自Thread时,就不能再继承其他类,使用Runnable接口解决了此问题,在新建一个Thread类中,在构造方法中初始化

Thread(Runnable target)
分配新的 Thread 对象。

Thread(Runnable target,String name)
分配新的 Thread 对象。

package com.bin.duoxiancheng;
public class D2 implements Runnable{
    int i=0;
    public void run(){
       for(i=0 ; i<50; i++){
           System.out.println(i);
           System.out.println(Thread.currentThread().getName());
           try {
              Thread.sleep(100);
           } catch (InterruptedException e) {
              // TODO Auto-generatedcatch block
              e.printStackTrace();
           }
       }
    }
    public static void main(String[] args){
       D2 d=new D2();
        Thread t=new Thread(d);
        t.start();
    }
}


线程中状态之间的转变

New状态,使用new创建的线程对象处于新建状态,仅仅在堆栈中分配了内存。

Runnable就绪状态。当线程调用start方法后,线程进入就绪状态。虚拟机会为他创建方法调用栈和程序计数器,处于这个状态的线程位于可运行池中,等待获得CPU使用权。

Running运行状态

这个状态的线程正在占用CPU,如果一个计算机只有一个CPU,那么这个时刻会有一个线程处于这个状态,如多个CPU,可有多个线程占用不同的CPU,只有处于就绪状态的线程才有机会变为运行状态。

阻塞状态(Blocked)

由于某些原因放弃CPU暂时停止运行,此时虚拟机不会给线程分配CPU,直到重新进入就绪状态,

阻塞状态可分为以下3种。

1、 当处于运行状态中的某个对象执行了wait()方法,虚拟机就把这个线程放到这个对象等待池中。

2、 当线程处于运行状态,试图获得某个对象的同步锁,若同步锁被其他线程占用,虚拟机会把这个线程放到这个对象的锁池中。

3、 当线程执行了sleep方法,或者调用join方法,或者发出了I/O请求时候,就会进入这个状态.

当线程执行System.out.println或者System.in.read()方法时,就会发出I/O请求,线程放弃cpu进入阻塞状态,直到I/O处理完成,

死亡状态(dead)

当线程退出run()方法时,就进入死亡状态,有可能正常 执行完run方法退出,也有可能异常退出,

Thread类中的isAlive()方法判断一个线程是否活着,当处于死亡状态或者新建状态时,该方法返回false,否则返回TRUE。

线程的调度

虚拟机按特定的机制为CPU分配使用权,主要有两种调度模式,分时调度模型和抢占式调度模型。分时调度模型就是线程轮流让CPU获得使用权,平均分配线程。抢占式调度为让线程中优先度高的占用CPU,若优先级相同,那么就随机选择一个。处于运行状态的线程会由以下原因放弃CPU

1、虚拟机转到就绪状态,其他线程获得机会。

2、线程进入阻塞状态;

3、线程运行结束

看以下程序:

package com.bin.duoxiancheng;
public class D3 extends Thread {
    public static int count=0;
    public static StringBuffer log=new StringBuffer();
    public void run(){
     for(int a=0;a<20;a++){
        log.append(currentThread().getName()+":"+a+" ");
        if(++count %10 ==0)log.append("\n");
     }
    }
    public static void main(String[] args) throws InterruptedException {
       // TODO Auto-generatedmethod stub
          D3 d1= new D3();
          D3 d2 =new D3();
          d1.setName("m1");
          d2.setName("d2");
          d1.start();
          d2.start();
          while(d1.isAlive()||d2.isAlive())
          Thread.sleep(500);
          System.out.print(log);
    }
}


结果为:

m1:0 m1:1 m1:2 m1:3 m1:4 m1:5m1:6 m1:7 m1:8 m1:9

m1:10 m1:11 m1:12 m1:13 m1:14m1:15 m1:16 m1:17 m1:18 m1:19

m2:0 m2:1 m2:2 m2:3 m2:4 m2:5m2:6 m2:7 m2:8 m2:9

m2:10 m2:11 m2:12 m2:13 m2:14 m2:15 m2:16m2:17 m2:18 m2:19

程序中m1先获得主动权,直到结束才交予m2,如果想让一个线程交给另外一个线程运行可以采取以下方法:

1、 调整优先级;调用Thread.setPriority(Thread.MAX_PRIORITY),参数取值范围为1-10,值越高优先度越高。

2、 调用Thread.sleep()方法;参数以毫秒为单位,当睡眠结束时候就会转为就绪状态,当另一个线程处于运行状态中,此线程在运行池 中处于等待状态

3、 调用Thread.yield()方法,如有有相同优先级别的线程处于就绪状态,那么yield把当前运行的线程放到可运行池中并使另一个线程运行;若没有相同级别的,则不运行,执行yield后,当前线程进入就绪状态,跟SLEEP不同,sleep为进入阻塞状态。

4、 调用JOIN方法;使用此方法后,当前程序进入阻塞状态。直至另一个程序结束才会恢复 运行。

package com.bin.duoxiancheng;
public class D5 extends Thread{
    public void run(){
       for(int a=0;a<50;a++){
           System.out.println(getName()+":"+a);
       }
    }
    public static void main(String[] args) throws InterruptedException {
       D5 d=new D5();
       d.setName("m1");
       d.start();
       System.out.println("main: joinmachine");
       d.join();
       System.out.println("main:end");
 
    }
}

System.out.println("main:end");这段程序要等到d这个线程结束后方执行。

也可以写作d.join(10)超过10毫秒恢复运行。

Timer类定时器的使用

参数使用如下:

(1)Timer.schedule(TimerTask task,Datetime)安排在制定的时间执行指定的任务。
(2)Timer.schedule(TimerTask task,Date firstTime ,long period)安排指定的任务在指定的时间开始进行重复的固定延迟执行.
(3)Timer.schedule(TimerTask task,long delay)安排在指定延迟后执行指定的任务.
(4)Timer.schedule(TimerTask task,long delay,long period)安排指定的任务从指定的延迟后开始进行重复的固定延迟执行. 第一个参数是要操作的方法,第二个参数是要设定延迟的时间,第三个参数是周期的设定,每隔多长时间执行该操作。
(5)Timer.scheduleAtFixedRate(TimerTask task,Date firstTime,long period)安排指定的任务在指定的时间开始进行重复的固定
速率执行.
(6)Timer.scheduleAtFixedRate(TimerTask task,l