黑马程序员——Java之多线程(1)

 

  ------Java培训、Android培训、iOS培训、Net培训、期待与您交流! -------

 

一、线程的基本概念

       一般来说,进程(Process)就是正在计算机中执行的程序。而线程(Thread)是进程中的某个单一顺序的控制流。线程是进程的细化,它是进程中的实体。进程是应用程序的运行实例,自己享有独立的地址空间。对于多任务操作系统,能够“同时”运行多个进程。但这些是CPU的分时机制在起作用,能够使每个进程都能循环获得自己的CPU时间片。进程(process)本质上是操作系统当前运行的执行程序。

       简单地说,一个程序至少有一个进程,一个进程至少有一个线程。进程在执行过程中拥有独立的内存单元,而对于线程来说,多个线程共享内存,因此极大地提高了程序的运行效率。

       使用多线程编程具有以下几点优点:

       (1)进程之间不能共享内存,但线程之间共享内存非常容易;

       (2)使用多线程来实现多任务并发比多进程的效率高;

       (3)Java语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了Java的多线程编程。

二、线程的创建和启动

        在Jvav中主要提供两种方式实现线程,分别为继承Thread类与实现Runnable接口。

  (1)通过继承Thread类创建和启动多线程

       Thread类存放在java.lang类库里,但并不需要加载java.lang类库,因为它会自动加载。run()方法是定义在Thread类里的一个方法,因此把线程的程序代码编写在run()方法内,事实上就是覆盖的操作。

      通过继承Thread类创建多线程的语法格式如下:

     

       继承Thread类方式的线程启动非常简单,只要创建线程类示例后调用其start()方法即可。

       启动线程的语法格式如下图:

      

示例:定义一个简单的线程类TextThread,在run()方法中利用for循环打印“@”。

//通过继承Thread类创建线程类ThreadDome
class ThreadDome extends Thread {
	//覆写run()方法
    public void run() {
    	for(int i=0;i< 10;i++)
    	{
    	     System.out.print("@");
        }
    }	
}
public class TextThread{
	public static void main(String[] args) {
//创建线程类ThreadDome的示例m,并以此调用start方法		
		ThreadDome m = new ThreadDome();
		m.start();
	}
	
}

控制台输出的内容:

@@@@@@@@@@

 

(2)通过实现Runnable接口创建和启动多线程

         如果自定义的线程类还要继承其他类,这时就不能采用第一种方式来创建。由于Java语言不支持类的多继承,却可以实现多个接口,所以这种情况可以采用实现Runnable接口的方式创建。

         通过实现Runnable接口创建多线程的语法格式:

        

        实现Runnable接口创建的线程首先转换为Thread类,然后调用Thread类的start()方法启动线程。

        启动线程的语法格式如下图:

       

示例:运用Runnable接口创建线程。

//通过实现Runnable接口创建线程类ThreadDome
class ThreadDome implements Runnable {
	//覆写run()方法
    public void run() {
    	for(int i=0;i< 10;i++)
    	{
    	     System.out.print("@");
        }
    }	
}
public class TextThread{
	public static void main(String[] args) {
//创建线程类ThreadDome的示例mt和线程类Thread的实例m,调用start()方法启动线程	
		ThreadDome mt = new ThreadDome();
		Thread m = new Thread(mt);
		m.start();
	}
}

控制台输出的内容:

@@@@@@@@@@

 

(3)继承Thread类与实现Runnable接口的区别

         继承Thread类:线程代码存放在Thread子类run()方法中;

         实现Runnable接口:线程代码存放在接口的子类的run()方法中。

 

三、线程的生命周期

       线程从其创建到死亡具有一个完整的生命周期,在整个生命周期中处于各种状态。线程的状态表明线程当前可以进行的活动。一个生命周期内的线程主要包括创建、就绪、运行、阻塞、死亡状态,如图所示。

      

(1)创建状态

        在线程类使用new关键字实例化之后且在调用start方法之前,线程处于创建状态。处于创建状态的线程仅仅分配了内存空间,属于生命周期的初始状态。
(2)就绪状态

        在线程调用了start方法后即处于就绪状态。处于就绪状态的线程具备了除CPU之外所有运行所需资源。就绪状态线程排队等待CPU,由系统调度为其分配。

        使线程处于就绪状态有以下几种可能:

        ①调用sleep()方法;

        ②调用wait()方法;

        ③等待输入/输出完成。

(3)运行状态

        处于就绪状态的线程获得CPU之后即处于运行状态。处于运行状态的线程才开始真正执行线程run方法的内容。

        使线程再次进入运行状态:

        ①线程调用notify()方法;

        ②线程调用notifyAll()方法;

        ③线程调用interrupt()方法;

        ④线程的休眠时间结束;

        ⑤输入/输出结束。

(4)阻塞/等待状态

        处于运行状态的线程如果因为某种原因不能继续执行,则进入阻塞状态。阻塞状态与就绪状态的不同是:就绪状态只是因为缺少CPU而不能执行,而阻塞状态是由于各种原因引起线程不能执行,不仅仅是缺少CPU。引起阻塞的原因解除以后,线程再次转为就绪状态,等待分配CPU运行。

(5)死亡状态

         线程会以以下3种方式结束,结束后就处于死亡状态:

         ①run()或call()方法完成,线程正常结束;

         ②线程抛出一个未捕获的Exception或Error;

         ③直接调用该线程的stop()方法来结束该线程——该方法容易导致死锁。

四、线程的调度

       处于生命周期中的线程,可以通过调度实现各种状态间的转换。线程的调度是使用各种线程调度方法,如设置优先级方法、睡眠方法、让步方法和等待方法等,对线程进行不同的操作。对于各种线程调度方法,下面分别进行介绍。

(1)join线程

        Thread提供了让一个线程等待另一个线程完成的方法——join()方法。当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完为止。

示例:

public class JoinThread extends Thread{
	//提供了一个有参数的构造器,用于设置改线程的名字
    public JoinThread (String name) {
		super(name);
	}
   //重写run()方法,定义了线程的执行体
    public void run() {
    	for (int i = 0; i < 100; i++) {
			System.out.println(getName()+""+i);
		}
    }
	public static void main(String[] args) throws InterruptedException {
		//启动子线程
		new JoinThread("新线程").start();
        for (int i = 0; i < 100; i++) {
			if (i==20) {
				JoinThread jt = new JoinThread("被Join的线程");
				jt.start();
				//main线程调用了jt线程的join()方法,main线程
				//需要等jt执行结束才会向下执行
				jt.join();
			}
			System.out.println(Thread.currentThread().getName() + "  " + i);
		}
	}

}


运行结果:

上面程序一共有3个线程,主方法开始时就启动了名为“新线程”的子线程,该子线程将会和main线程并发执行。当主线程的循环变量i=20时,启动了名为“被join的线程”的线程,该线程不会和mian线程并发执行,main线程必须等该线程执行结束后才可以向下执行。

 

(2)线程的睡眠:sleep

        Thread类中提供了sleep()方法,可以让当前线程休眠(停止执行)一定的时间,线程由运行中状态进入阻塞状态 ,等待停止执行时间到后,线程再进入可运行状态。

示例:以下程序的实际效果是打印0~9的数字,其中,每隔1秒打印一个数字,在程序运行时可以清楚地看出sleep()方法的作用。

public class SleepText extends Thread {
	//重写run()方法,定义了线程的执行体
    public void run() {
    	for (int i = 0; i < 10; i++) {
			System.out.print(i);
			try {
				//设定线程睡眠的时间
				//由于线程休眠时可能被中断,所以需要捕获异常
				sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
    }
	public static void main(String[] args) {
		SleepText st = new SleepText();
		//启动线程
		st.start();
   }
}

运行结果:

0  1  2  3  4  5  6  7  8  9  


(3)线程的让步:yield

        yield()方法是一个和sleep()方法有点相似的方法,它是Thread类提供的一个静态方法。对于正在执行的线程,可以调用yield()方法使其重新排队,将CPU让给排在后面的线程,此线程转为就绪态。另外,yield()方法只让步给高优先级或同等优先级的线程,如果后面是低优先级线程,则继续执行此线程。yield没有声明抛出任何异常。

示例:两个线程交替打印0~9的数字

public class ThreamDome extends Thread {
    //重写run()方法,定义了线程的执行体
    public void run() {
    	for (int i = 0; i < 10; i++) {
		System.out.print(i);
		//调用yield()方法让当前线程让步
		yield();
		}
    }
	public static void main(String[] args) {
	    ThreamDome m1 = new ThreamDome();
	    ThreamDome m2 = new ThreamDome();
	    //启动线程
	    m1.start();
	    m2.start();
	    }
}

运行结果:

01023456789123456789

 

(4)线程的优先级

        线程优先级是指线程在被系统调度执行时的优先执行级别。在多线程程序中,往往是多个线程同时等待被调度执行。然而,每个线程的重要程度通常是不一样的,在同等条件下,有些重要线程需要优先执行。在Java语言中,Thread类提供了setPriority(int  newPriority)、其中setPriority()方法的参数可以是一个整数,范围是1~10之间,也可以使用Thread类的如下3个静态常亮。

        ①MAX_PRIORITY:其值是10

        ②MIN_PRIORITY:其值是1

        ③NORM_PRIORITY:其值是5

        如果没有为线程设置优先级,则线程的优先级默认为5。

 示例:    

public class PriorityTest extends Thread{
    //定义一个有参数的构造器,用于创建线程时指定name
	public PriorityTest(String name) {
		super(name);
	}
	//重写run()方法,定义了线程的执行体
	public void run() {
		for (int i = 0; i < 50; i++) {
			System.out.println(getName() + ",其优先级是:"
					+ getPriority() + ",循环变量的值为:" + i);
		}
	}
	public static void main(String[] args) {
		//改变主线程的优先级
		Thread.currentThread().setPriority(6);
		for (int i = 0; i < 30; i++) {
			if (i == 10) {
				PriorityTest low = new PriorityTest("低级");
				low.start();
				System.out.println("创建之初的优先级:" + low.getPriority());
				//设置了该线程为最低优先级
				low.setPriority(Thread.MIN_PRIORITY);
				
			}
		if (i == 20) {
			PriorityTest high = new PriorityTest("高级");
				high.start();
				System.out.println("创建之初的优先级:" + high.getPriority());
				//设置了该线程为最高优先级
				high.setPriority(Thread.MAX_PRIORITY);
		}
	  }
   }
}


运行结果 :

创建之初的优先级:6
创建之初的优先级:6
高级,其优先级是:10,循环变量的值为:0
高级,其优先级是:10,循环变量的值为:1
高级,其优先级是:10,循环变量的值为:2
高级,其优先级是:10,循环变量的值为:3
高级,其优先级是:10,循环变量的值为:4
高级,其优先级是:10,循环变量的值为:5
高级,其优先级是:10,循环变量的值为:6
高级,其优先级是:10,循环变量的值为:7
高级,其优先级是:10,循环变量的值为:8
高级,其优先级是:10,循环变量的值为:9
高级,其优先级是:10,循环变量的值为:10
高级,其优先级是:10,循环变量的值为:11
高级,其优先级是:10,循环变量的值为:12
高级,其优先级是:10,循环变量的值为:13
高级,其优先级是:10,循环变量的值为:14
高级,其优先级是:10,循环变量的值为:15
高级,其优先级是:10,循环变量的值为:16
高级,其优先级是:10,循环变量的值为:17
高级,其优先级是:10,循环变量的值为:18
高级,其优先级是:10,循环变量的值为:19
高级,其优先级是:10,循环变量的值为:20
高级,其优先级是:10,循环变量的值为:21
高级,其优先级是:10,循环变量的值为:22
高级,其优先级是:10,循环变量的值为:23
高级,其优先级是:10,循环变量的值为:24
高级,其优先级是:10,循环变量的值为:25
高级,其优先级是:10,循环变量的值为:26
高级,其优先级是:10,循环变量的值为:27
高级,其优先级是:10,循环变量的值为:28
高级,其优先级是:10,循环变量的值为:29
高级,其优先级是:10,循环变量的值为:30
高级,其优先级是:10,循环变量的值为:31
高级,其优先级是:10,循环变量的值为:32
高级,其优先级是:10,循环变量的值为:33
高级,其优先级是:10,循环变量的值为:34
高级,其优先级是:10,循环变量的值为:35
高级,其优先级是:10,循环变量的值为:36
高级,其优先级是:10,循环变量的值为:37
高级,其优先级是:10,循环变量的值为:38
高级,其优先级是:10,循环变量的值为:39
高级,其优先级是:10,循环变量的值为:40
高级,其优先级是:10,循环变量的值为:41
高级,其优先级是:10,循环变量的值为:42
高级,其优先级是:10,循环变量的值为:43
高级,其优先级是:10,循环变量的值为:44
高级,其优先级是:10,循环变量的值为:45
高级,其优先级是:10,循环变量的值为:46
高级,其优先级是:10,循环变量的值为:47
高级,其优先级是:10,循环变量的值为:48
高级,其优先级是:10,循环变量的值为:49
低级,其优先级是:1,循环变量的值为:0
低级,其优先级是:1,循环变量的值为:1
低级,其优先级是:1,循环变量的值为:2
低级,其优先级是:1,循环变量的值为:3
低级,其优先级是:1,循环变量的值为:4
低级,其优先级是:1,循环变量的值为:5
低级,其优先级是:1,循环变量的值为:6
低级,其优先级是:1,循环变量的值为:7
低级,其优先级是:1,循环变量的值为:8
低级,其优先级是:1,循环变量的值为:9
低级,其优先级是:1,循环变量的值为:10
低级,其优先级是:1,循环变量的值为:11
低级,其优先级是:1,循环变量的值为:12
低级,其优先级是:1,循环变量的值为:13
低级,其优先级是:1,循环变量的值为:14
低级,其优先级是:1,循环变量的值为:15
低级,其优先级是:1,循环变量的值为:16
低级,其优先级是:1,循环变量的值为:17
低级,其优先级是:1,循环变量的值为:18
低级,其优先级是:1,循环变量的值为:19
低级,其优先级是:1,循环变量的值为:20
低级,其优先级是:1,循环变量的值为:21
低级,其优先级是:1,循环变量的值为:22
低级,其优先级是:1,循环变量的值为:23
低级,其优先级是:1,循环变量的值为:24
低级,其优先级是:1,循环变量的值为:25
低级,其优先级是:1,循环变量的值为:26
低级,其优先级是:1,循环变量的值为:27
低级,其优先级是:1,循环变量的值为:28
低级,其优先级是:1,循环变量的值为:29
低级,其优先级是:1,循环变量的值为:30
低级,其优先级是:1,循环变量的值为:31
低级,其优先级是:1,循环变量的值为:32
低级,其优先级是:1,循环变量的值为:33
低级,其优先级是:1,循环变量的值为:34
低级,其优先级是:1,循环变量的值为:35
低级,其优先级是:1,循环变量的值为:36
低级,其优先级是:1,循环变量的值为:37
低级,其优先级是:1,循环变量的值为:38
低级,其优先级是:1,循环变量的值为:39
低级,其优先级是:1,循环变量的值为:40
低级,其优先级是:1,循环变量的值为:41
低级,其优先级是:1,循环变量的值为:42
低级,其优先级是:1,循环变量的值为:43
低级,其优先级是:1,循环变量的值为:44
低级,其优先级是:1,循环变量的值为:45
低级,其优先级是:1,循环变量的值为:46
低级,其优先级是:1,循环变量的值为:47
低级,其优先级是:1,循环变量的值为:48
低级,其优先级是:1,循环变量的值为:49


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值