线程基础

一、线程的基本概念
       在操作系统中两个比较容易混淆的概念是进程(process)和线程(thread)。操作系统中的进程是资源的组织单位。进程有一个包含了程序内容和数据的地址空间,以及其它的资源,包括打开的文件、子进程和信号处理器等。不同进程的地址空间是互相隔离的。而线程表示的是程序的执行流程,是CPU调度的基本单位。线程有自己的程序计数器、寄存器、栈和帧等。引入线程的动机在于操作系统中阻塞式I/O的存在。当一个线程所执行的I/O被阻塞的时候,同一进程中的其它线程可以使用CPU来进行计算。这样的话,就提高了应用的执行效率。线程的概念在主流的操作系统和编程语言中都得到了支持。
       一部分的Java程序是单线程的。程序的机器指令按照程序中给定的顺序依次执行。Java语言提供了java.lang.Thread类来为线程提供抽象。有两种方式创建一个新的线程:一种是继承java.lang.Thread类并覆写其中的run()方法,另外一种则是在创建java.lang.Thread类的对象的时候,在构造函数中提供一个实现了java.lang.Runnable接口的类的对象。在得到了java.lang.Thread类的对象之后,通过调用其start()方法就可以启动这个线程的执行。
       一个线程被创建成功并启动之后,可以处在不同的状态中。这个线程可能正在占用CPU时间运行;也可能处在就绪状态,等待被调度执行;还可能阻塞在某个资源或是事件上。多个就绪状态的线程会竞争CPU时间以获得被执行的机会,而CPU则采用某种算法来调度线程的执行。不同线程的运行顺序是不确定的,多线程程序中的逻辑不能依赖于CPU的调度算法。
  Windows操作系统是支持多线程的,它可以同时执行很多个线程,也支持多进程,因此Windows操作系统是支持多线程多进程的操作系统。Linux和Uinux也是支持多线程和多进程的操作系统。DOS就不是支持多线程和多进程了,它只支持单进程,在同一个时间点只能有一个进程在执行,这就叫单线程。
  CPU难道真的很神通广大,能够同时执行那么多程序吗?不是的,CPU的执行是这样的:CPU的速度很快,一秒钟可以算好几亿次,因此CPU把自己的时间分成一个个小时间片,我这个时间片执行你一会,下一个时间片执行他一会,再下一个时间片又执行其他人一会,虽然有几十个线程,但一样可以在很短的时间内把他们通通都执行一遍,但对我们人来说,CPU的执行速度太快了,因此看起来就像是在同时执行一样,但实际上在一个时间点上。
  那什么才是真正的多线程?如果你的机器是双CPU,或者是双核,这确确实实是多线程。

二、线程的创建和启动

  在JAVA里面,JAVA的线程是通过java.lang.Thread类来实现的,每一个Thread对象代表一个新的线程。创建一个新线程出来有两种方法:第一个是从Thread类继承,另一个是实现接口runnable。VM启动时会有一个由主方法(public static void main())所定义的线程,这个线程叫主线程。可以通过创建Thread的实例来创建新的线程。你只要new一个Thread对象,一个新的线程也就出现了。每个线程都是通过某个特定的Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。

Demo1:实现Runnable接口创建和启动新线程
  1. package com.liangdianshui;  
  2.   
  3. public class ThreadRunable {  
  4.     public static void main(String args[]) {  
  5.         MyRunable mRunable = new MyRunable();  
  6.         // r1.run();//这个称为方法调用,方法调用的执行是等run()方法执行完之后才会继续执行main()方法  
  7.         new Thread(mRunable).start();  //启动一个线程  
  8.         for (int i = 0; i < 10; i++) {  
  9.             System.out.println("main:" + i);  
  10.         }  
  11.     }  
  12. }  
  13.   
  14. /** 
  15.  * 自定义一个类,实现Runable接口 打印1-10 
  16.  * 
  17.  */  
  18. class MyRunable implements Runnable {  
  19.     public void run() {  
  20.         for (int i = 0; i < 10; i++) {  
  21.             System.out.println("MyRunable:" + i);  
  22.         }  
  23.     }  
  24. }  
运行结果:

 在我的机子上运行的结果是这样,在你们的机子上运行的结果也可能不一样的!他的运行原理是这样的:


Demo2:继承Thread类,重run()方法创建和启动新的线程
[java]  view plain  copy   在CODE上查看代码片 派生到我的代码片
  1. package com.liangdianshui;  
  2.   
  3. public class ThreadExtendThread {  
  4.     public static void main(String[] args) {  
  5.         MyThread mThread = new MyThread();  
  6.         mThread.start(); // 开启一个新的线程  
  7.         for (int i = 0; i < 10; i++) {  
  8.             System.out.println("main:" + i);  
  9.         }  
  10.     }  
  11. }  
  12.   
  13. /** 
  14.  * 继承Thread 
  15.  * 
  16.  */  
  17. class MyThread extends Thread {  
  18.     @Override  
  19.     public void run() {  
  20.         super.run();  
  21.         for (int i = 0; i < 10; i++) {  
  22.             System.out.println("MyThread: " + i);  
  23.         }  
  24.     }  
  25. }  
  26. 使用实现Runnable接口和继承Thread类这两种开辟新线程的方法,在选择上:应该优先选择实现Runnable接口这种方式去开辟一个新的线程。因为接口的实现可以实现多个,而类的继承只能是单继承。因此在开辟新线程时能够使用Runnable接口就尽量不要使用从Thread类继承的方式来开辟新的线程。

           通过上面的例子,我们可以这样理解线程,主线程就是一个部门的主管,当遇到比较耗时的工作的时候,他可以安排部门的其他员工帮忙做,然后他自己继续做下面的事情,最后只要知道员工完成的结果就行了~!

    三、线程状态转换
    3.1.线程控制的基本方法
    3.2. sleep/join/yield方法介绍

    Demo

    [java]  view plain  copy   在CODE上查看代码片 派生到我的代码片
    1. package com.liangdianshui;  
    2.   
    3. import java.util.Date;  
    4.   
    5. public class ThreadRunable {  
    6.     public static void main(String args[]) {  
    7.         MyRunable mRunable = new MyRunable();  
    8.         // r1.run();//这个称为方法调用,方法调用的执行是等run()方法执行完之后才会继续执行main()方法  
    9.         new Thread(mRunable).start(); // 启动一个线程  
    10.         for (int i = 0; i < 10; i++) {  
    11.             System.out.println("main:" + i);  
    12.             if (i == 5) {  
    13.                 try {  
    14.                     System.out.println(new Date().toLocaleString());  //打印当前时间  
    15.                     Thread.sleep(5000); // 让主线程休眠5秒  
    16.                     System.out.println(new Date().toLocaleString());  //打印当前时间  
    17.                 } catch (InterruptedException e) {  
    18.                     e.printStackTrace();  
    19.                 }  
    20.             }  
    21.         }  
    22.     }  
    23. }  
    24.   
    25. /** 
    26.  * 自定义一个类,实现Runable接口 打印1-10 
    27.  * 
    28.  */  
    29. class MyRunable implements Runnable {  
    30.     public void run() {  
    31.         for (int i = 0; i < 10; i++) {  
    32.             System.out.println("MyRunable:" + i);  
    33.         }  
    34.     }  
    35. }  

    运行结果:

    Demo:

    [java]  view plain  copy   在CODE上查看代码片 派生到我的代码片
    1. package com.liangdianshui;  
    2.   
    3. import java.util.Date;  
    4.   
    5. public class ThreadRunable {  
    6.     public static void main(String args[]) {  
    7.         MyRunable mRunable = new MyRunable();  
    8.         // r1.run();//这个称为方法调用,方法调用的执行是等run()方法执行完之后才会继续执行main()方法  
    9.         Thread mThread = new Thread(mRunable);  
    10.         mThread.start(); // 启动一个线程  
    11.         for (int i = 0; i < 10; i++) {  
    12.             System.out.println("main:" + i);  
    13.         }  
    14.         try {  
    15.             mThread.join();  //把子线程合并到主线程,等于是调用子线程的方法  
    16.         } catch (InterruptedException e) {  
    17.             e.printStackTrace();  
    18.         }  
    19.     }  
    20. }  
    21.   
    22. /** 
    23.  * 自定义一个类,实现Runable接口 打印1-10 
    24.  * 
    25.  */  
    26. class MyRunable implements Runnable {  
    27.     public void run() {  
    28.         for (int i = 0; i < 10; i++) {  
    29.             System.out.println("MyRunable:" + i);  
    30.             if (i == 2) {  
    31.                 try {  
    32.                     Thread.sleep(2000);  
    33.                 } catch (InterruptedException e) {  
    34.                     // TODO Auto-generated catch block  
    35.                     e.printStackTrace();  
    36.                 }  
    37.             }  
    38.         }  
    39.     }  
    40. }  

    运行结果:

     join方法你可以理解为是直接停止了那个子线程,然后在主线程中继续运行!

    Demo:
    [java]  view plain  copy   在CODE上查看代码片 派生到我的代码片
    1. package com.liangdianshui;  
    2.   
    3. import java.util.Date;  
    4.   
    5. public class ThreadRunable {  
    6.     public static void main(String args[]) {  
    7.         MyThread1 t1 = new MyThread1("t1");  
    8.         MyThread1 t2 = new MyThread1("t2");  
    9.         t1.start();  
    10.         t2.start();  
    11.     }  
    12. }  
    13.   
    14. class MyThread1 extends Thread {  
    15.   
    16.     private String strName;  
    17.   
    18.     public MyThread1(String str) {  
    19.         strName = str;  
    20.     }  
    21.   
    22.     public void run() {  
    23.         for (int i = 0; i < 10; i++) {  
    24.             System.out.println(strName + ":" + i);  
    25.             try {  
    26.                 Thread.sleep(1000); //为了更好的显示效果,增加延迟  
    27.             } catch (InterruptedException e) {  
    28.                 // TODO Auto-generated catch block  
    29.                 e.printStackTrace();  
    30.             }  
    31.             if (i % 2 == 0) {  
    32.                 yield();  
    33.             }  
    34.         }  
    35.     }  
    36.   
    37. }  
    四、线程的优先级别

    五、线程同步


    六、总结
            这篇博文首先简单的介绍了什么是进程和线程,知道单个CPU不是真正意义上的多进程,CPU把自己的时间分成一个个小时间片,我这个时间片执行你一会,下一个时间片执行他一会,加上CPU的运行速度很快,因此像同时运行多个程序一样,然后了解实现Thread的两种方法,一种是实现Runable接口,一种是继承Thread类,最后讲了线程的一些方法!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值