Java多线程

                           Java的多线程机制

一、进程与线程的区别:

进程是指正在操作系统中运行的程序或软件。一个进程就代表一个已经启动并正在运行的程序。

线程:一个程序的执行路径,若此路径只有一条,则是单线程,若像迅雷这种软件(或程序),一次可以有5个执行路径,则称为多线程。

 

    Windows操作系统是[多进程]  _ 【多线程】   的系统。

 

 

 

1.       线程的概念:线程是一个系统中不同的执行路径。

   

package threaddemo;

 

/**

 * 在这个程序中,main方法调用了m1方法,m1方法又调用了m2,m3方法。

 * @author Administrator

 *

 */

public class Demo1 {

 

    public static void main(String[] args) {

       m1();

    }

 

    private static void m1() {

       m2();

       m3();

      

    }

 

    private static void m3() {

    }

 

    private static void m2() {

    }

}

 

下面是此程序的执行路径 。是一条线,是一条执行路径。

程序在执行过程中每有一个分支,就会产生一个线程。

Main方法是一个程序执行的一个主分支,所以也称为主线程。

二、线程和进程。

进程是一个静态的概念。机器上的一个.class文件,一个.exe的可执行文件,这是一个进程。

程序的执行过程是第一步先把代码放入内存。放进去之后并没有马上执行。此时说明一个进程已经产生,进程已经准备开始了。但是还没有开始,这叫一个进程 。进程本身不能动。我们平时所说的进程的执行是指进程中主线程开始执行了。即main方法开始执行了。

在我们的机器当中运行的都是线程。

现在我们的windows操作系统可以同时运行多个可执行程序,即可以同时有多个进程启动(可以查看任务管理器),但需要明白的一点是这些进程只是静态概念,机器中真正在执行任务的是每一个进程中的多个线程。所以windows可以理解为是多进程多线程的操作系统。

当然Linux,unix也是,只有DOS不是。

 

三、分析CPU执行程序的原理。

CPU把自己的时间分成了一个个的时间片。每个时间片分配给不同程序的不同线程。然后轮流执行。因为速度快的原因,使人感觉像是在同时运行。但真正在一个时间点上只有一个程序的一个线程在运行。(只有双CPU或双核是真正的同时运行)

 

四、如何开始一个新的执行路径(即如何开始一个线程)

Java中的线程是通过 java.lang.Thread这个类来实现的。每一个Thread的对象代表一个线程。

创建线程和启动线程需要二步来执行。

第一步创建线程:有两种创建方式:

1.创建一个类继承Thread类,并重写run()方法,在此方法中写上你的程序要执行的任务。实例化这个自定义的类的对象,然后调用start()方法即可。

2.       创建一个实现了 Runnable接口的类,实例化这个自定义的类的对象,然后把这个对象作为new一个Thread类的对象时所需传递的参数传递给thread类的构造方法。

3.       每一个线程对象都是通过run()方法完成其功能。

4.       但线程的启动并不是直接调用run()方法,而是通过调用线程对象的start方法而间接调用run() 方法。注意:启动一个线程不是简单的一个run方法调用。

注意:因为Thread类和runnable接口中都有run()方法,run()方法是唯 一可以定义我们自己业务任务的地方。所以一定要重写与实现run()方法。

观察Thread类的下列构造方法:

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

这里所传参数是Runnable这个接口的类型,我们这里需要传的是Runnable接口的实现类对象,即父类引用指向子类对象。这里也是多态的体现。既然是子类的对象,当然调用的就是子类重写的run方法。

观察代码:

public class TestThread1 {

       public static void main(String args[]) {

              Runner1 r = new Runner1();

              r.start();

              //r.run();

              //Thread t = new Thread(r);

              //t.start();

             

              for(int i=0; i<100; i++) {

                     System.out.println("Main Thread:------" + i);

              }

       }

}

 

//class Runner1 implements Runnable {

class Runner1 extends Thread {

       public void run() {

              for(int i=0; i<100; i++) {     

                     System.out.println("Runner1 :" + i);

              }

       }

}

分析:当main方法执行到start()方法时,一个程序的分支,即一个新的线程产生了。

而方法调用是下列执行流程:

 

但要注意:start()方法只是表明子线程已经准备好了,等待CPU分配时间片来调用。另一方面,CPU分给每个线程的时间片的时间长短也并不均匀,所以每一个线程的运行时间长短也不相同。但总是运行规律是两个线程或多个线程是交替运行。

 

五.面对两种创建线程的方式,该选择哪种方式?

应该选择实现接口,因为java只允许单继承,你从Thread类中继承了,就不可以再从其他类继承了。

 

六、线程的状态及状态转换。

本身一个线程对象被new出来以后,就创建好了。但不能运行

接下来调用start方法,但此方法被调用后也并不会马上执行,这只表明此线程已经准备就绪了,进入就绪状态。因为CPU此时可能正在运行其他线程,所以并不会马上切换过来来运行新线程。当CPU切换过来调度新线程时,则新线程进入运行状态。但这也并不表明新线程可以一直运行到任务全部结束为止。当新线程运行完CPU分配给他的时间片后,就会再次回到就绪状态等待。这是一个不断往复的过程,至到任务全部运行结束则进入终止状态。但在此过程中若发生了一些特殊情况,导致线程任务的运行受到阻塞,那就进入阻塞状态,直到阻塞状态解除后,重新进入就绪状态,再次等待CPU调度运行。这样往复直至任务全部结束。

 

 

七:如何控制线程状态的转换:

1.       isAlive():除了终止状态和线程在调用start()方法之前,其他状态下都可以理解为线程“活”着。

package threaddemo;

 

/**

 * 在这个程序中,main方法调用了m1方法,m1方法又调用了m2,m3方法。

 * @author Administrator

 *

 */

public class Demo1 {

 

    public static void main(String[] args)  {

       A a=new A();

       Thread aThread=new Thread(a);

       aThread.setName("A线程");

       aThread.start();

       System.out.println(aThread.isAlive());

       try {

           Thread.sleep(2000);

        } catch (InterruptedException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }

       System.out.println(aThread.isAlive());

    }

   

}

 

class A implements Runnable{

 

    public void run() {

       for(int i=0;i<20;i++){

           System.out.println(i);

       }

      

      

    }

   

}

 

 

2.       setPriority():线程是有优先级的。所谓优先级是说:优先级越高的线程所获得的CPU的运行时间越多。但优先级低的线程也是有执行机会的。但这种机会相比会显示“来之不易”。

3.       sleep():当前线程睡眠的毫秒数。

4.       join():合并线程。在主线程中调用子线程对象的join()方法,就是主线程要等待子线程执行完毕才能继续执行(相当于两个线程合并成一个),这会导致子线程顺序先于主线程顺序。

5.       yield():让出CPU,给其他线程执行的机会(只是让出一下,但只要一调用此方法,就会马上切换成另一个线程)

6.       wait,notify,notifyAll,:线程同步中会涉及。

 

八、线程调度方法的详解:

1.Sleep():Thread类的静态方法。

static void

sleep(long millis)
          
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。

Sleep(可以通过此方法调整程序执行的快慢。例如每隔1000毫秒显示一次当前时间)

public static void sleep(long millis)

                  throws InterruptedException

在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。该线程不丢失任何监视器的所属权。

参数:

millis - 以毫秒为单位的休眠时间。

抛出:

InterruptedException - 如果另一个线程中断了当前线程。当抛出该异常时,当前线程的中断状态 被清除。

当睡眠之中的线程被别的线程打断了就会抛出此异常。

import java.util.*;

public class TestInterrupt {

  public static void main(String[] args) {

    MyThread thread = new MyThread();

    thread.start();

    try {Thread.sleep(10000);}//主线程睡眠了10秒钟,这样就使子线程有了10秒钟的运行时间,子线程运行了10秒,把当前的时间打印了出来。

    catch (InterruptedException e) {}

    thread.interrupt();//10秒后,主线程调用了子线程的打扰方法,给子线程浇了一盆凉水。使子线程发生InterruptException,子线程执行catch块中的return方法结束程序,因此这句代码就可以让子线程结束。这是一种让线程停止的方式,但并不是最好的方式。这里注意更不应该使用stop方法,因为此方法会使线程立刻停止,而不会给线程一些收尾释放资源的机会。

  }

}

 

class MyThread extends Thread {

       boolean flag = true;

  public void run(){//子线程任务区域

    while(flag){

      System.out.println("==="+new Date()+"===");

      try {

        sleep(1000);//子线程睡眠了。

      } catch (InterruptedException e) {

        return;// 表示结束

//flag = false;//比较好的处理方式

      }

    }

  }

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值