首页 > 技术文章 > 认识多线程(一)

szrs 2019-11-13 13:45 原文

卷首语

欲练此功,不必自宫。

不保证不粘贴,不复制,但保证,每句话都是自己的理解。

前言

要学习并应用多线程,线程池的知识,首先得认识什么是线程,然后才是为什么要用多线程?程序在不用多线程的情况下,可以正常开发,运行,肯定没必要。一定是在工作,开发的过程中,接触到了,你才会去学习它。

我们必须得承认,在互联网高速发展的网络社会环境下,效率是我们越来越关注的点,程序在短时间,处理任务的能力,也越来越考验代码的水平。

一、心法篇

1、什么是线程

  • 程序:由代码组成,未执行(由一系列指令组成的有序集合),占用硬盘的存储空间。
  • 进程:正在运行中的代码。可以理解为受操作系统管理的基本运行单元。360浏览器是一个进程WPS也是一个进程,正在操作系统中运行的".exe"都可以理解为一个进程;进程有三部分:CPU,DATA,CORE  。
  • 线程:进程中独立运行的子任务。像QQ.exe运行的时候就有很多子任务在运行,比如聊天线程、好友视频线程、下载文件线程等等。
  • 进程与线程之间的关系:一个进程包含多个线程,每个进程都是都是独立的。进程结束,线程一定结束;线程结束,进程未必结束。系统为进程分配内存资源,线程共享进程的内存资源。
CPU调度执行线程

2、有什么用

如果使用得当,线程可以有效地降低程序的开发和维护等成本,同时提升复杂应用程序的性能。具体说,线程的优势有:

  • 发挥多处理器的强大能力

现在,多处理器系统正日益盛行,并且价格不断降低,即使在低端服务器和中断桌面系统中,通常也会采用多个处理器,这种趋势还在进一步加快,因为通过提高时钟频率来提升性能已变得越来越困难,处理器生产厂商都开始转而在单个芯片上放置多个处理器核。试想,如果只有单个线程,双核处理器系统上程序只能使用一半的CPU资源,拥有100个处理器的系统上将有99%的资源无法使用。多线程程序则可以同时在多个处理器上执行,如果设计正确,多线程程序可以通过提高处理器资源的利用率来提升系统吞吐率。

  • 在单处理器系统上获得更高的吞吐率

如果程序是单线程的,那么当程序等待某个同步I/O操作完成时,处理器将处于空闲状态。而在多线程程序中,如果一个线程在等待I/O操作完成,另一个线程可以继续运行,使得程序能在I/O阻塞期间继续运行。

  • 建模的简单性(防止阻塞)(需要在具体场景进行理解,代码举例)

通过使用线程,可以将复杂并且异步的工作流进一步分解为一组简单并且同步的工作流,每个工作流在一个单独的线程中运行,并在特定的同步位置进行交互。我们可以通过一些现有框架来实现上述目标,例如Servlet和RMI,框架负责解决一些细节问题,例如请求管理、线程创建、负载平衡,并在正确的时候将请求分发给正确的应用程序组件。编写Servlet的开发人员不需要了解多少请求在同一时刻要被处理,也不需要了解套接字的输入流或输出流是否被阻塞,当调用Servlet的service方法来响应Web请求时,可以以同步的方式来处理这个请求,就好像它是一个单线程程序。

zs理解:客户端向服务器发送请求后,服务器以多线程高效的处理请求,并以同步的方式来响应客户端。

  • 异步事件的简化处理(不理解,需要具体场景理解,场景举例)

服务器应用程序在接受多个来自远程客户端的套接字连接请求时,如果为每个连接都分配其各自的线程并且使用同步I/O,那么就会降低这类程序的开发难度。如果某个应用程序对套接字执行读操作而此时还没有数据到来,那么这个读操作将一直阻塞,直到有数据到达。在单线程应用程序中,这不仅意味着在处理请求的过程中将停顿,而且还意味着在这个线程被阻塞期间,对所有请求的处理都将停顿。为了避免这个问题,单线程服务器应用程序必须使用非阻塞I/O,但是这种I/O的复杂性要远远高于同步I/O,并且很容易出错。然而,如果每个请求都拥有自己的处理线程,那么在处理某个请求时发生的阻塞将不会影响其他请求的处理。

 3、优缺点

  • 查询:重效率;此时可以采用多线程。
  • 购票:数据准确,重线程的安全性,需要对共享资源进行加锁,否则容易出现获取的数据不准确;

二、修炼

修炼功法,切记走火入魔。实现方法有两种:

  • 继承Thread类,重写run方法。
package com.asd.reserve.pojo.basic;

/**
 * @author zs
 * @date 2019/9/20 9:02
 */
public class MyThread  extends Thread{//【1】继承了Thread类,具有了多线程操作的能力
    //【2】重写run方法
    @Override
    public void run() {
        //【3】线程体  (为了让大家看到的运行效果更明显,使用循环)
        for (int i = 0; i <10; i++) {
            System.out.println("MyThread类中的i------------->"+i);
        }
    }
}

package com.asd.reserve.pojo.basic;

/**
 * @author zs
 * @date 2019/9/20 9:00
 */
public class TestMyThread {

    public static void main(String[] args) {
        //【1】创建多线程类MyThread的对象
        MyThread my=new MyThread();
        //【2】启动多线程
        my.start();//执行线程体,线程体写在了run方法中
        /**以下循环是主线程中的代码*/
        for (int i = 0; i <10; i++) {
            //System.out.println("main中的i===========================>"+i);
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "中的" + i);
        }
    }
}
  • 实现Runnable接口,实现run方法。
public class MyThread01 implements Runnable
{
    public void run()
    {
        for (int i = 0; i < 5; i++)
        {
            System.out.println(Thread.currentThread().getName() + "在运行!");
        }
    }
}

public static void main(String[] args)
{
    MyThread01 mt0 = new MyThread01();
    Thread t = new Thread(mt0);
    t.start();
        
    for (int i = 0; i < 5; i++)
    {
        System.out.println(Thread.currentThread().getName() + "在运行!");
    }
}
  • 运行结果
main在运行!
Thread-0在运行!
main在运行!
Thread-0在运行!
main在运行!
Thread-0在运行!
main在运行!
Thread-0在运行!
main在运行!
Thread-0在运行!

可以明显的看到主线程main与多线程中的run()方法交替运行。

有可能有些人看不到这么明显的效果,这也很正常。所谓的多线程,指的是两个线程的代码可以同时运行,而不必一个线程需要等待另一个线程内的代码执行完才可以运行。对于单核CPU来说,是无法做到真正的多线程的,每个时间点上,CPU都会执行特定的代码,由于CPU执行代码时间很快,所以两个线程的代码交替执行看起来像是同时执行的一样。那具体执行某段代码多少时间,就和分时机制系统有关了。分时系统把CPU时间划分为多个时间片,操作系统以时间片为单位片为单位各个线程的代码,越好的CPU分出的时间片越小。所以看不到明显效果也很正常,一个线程打印5句话本来就很快,可能在分出的时间片内就执行完成了。所以,最简单的解决办法就是把for循环的值调大一点就可以了(也可以在for循环里加Thread.sleep方法)。

百言不如一实践,通过代码,直观的认识什么是多线程。就是在一个主方法调用里,互不干涉地同时执行两个(或多个)逻辑代码,我们可以将这理解为一个进程里同时执行多个子任务。打个比喻:上班坐地铁,大家都体验过,过安检。主方法就是:安检员说:大家可以过了(执行主方法),人少的时候,只需要一个安检口,就可以安检过来所有乘客,这个安检通道就是我们程序里的main方法;高峰期人太多,就需要多开几个安检口,这几个安检口都可以过人,两个(或多个)互不干涉的方法。而高峰期新开的安检通道,就是我们程序里多线程Thread里的run()方法。

  • 启动实现Runnable接口的多线程用到了代理设计模式,如下图:

 

  • 两者的利弊、区别:继承Thread类有局限性,继承Thread类之后就无法继承其他类了;多线程在访问共享资源时,继承Thread类时,无法实现资源的共享。

三、练功图

  • 线程的生命周期

  • 关于线程的生命周期:

四、切磋

1、Runnable可以实现资源共享但Thread不能实现资源共享的原因

通过Thread实现线程:

//使用Thread实现线程不能实现资源共享  
class MyThread extends Thread  
{     
    private int ticket=5;  
    private String name;  
    public MyThread(String name ){  
        this.name=name;  
    }  
    public void run(){  
        for(int i=0;i<10;i++){  
            if(ticket>5){  
                System.out.println("线程"+name+"卖票"+i);  
            }  
        }  
      
    }  
}  
  
public class ThreadDemo02  
{  
    public static void main(String args[]){  
        MyThread A = new MyThread("A");    
        MyThread B = new MyThread("B");  
        A.start();  
        B.start();  
    }  
}  

通过Runnable实现:

//使用Runnable实现线程可以实现资源共享  
class MyThread implements Runnable  
{  
    private int ticket=5;  
    private String name;  
    public MyThread(String name){  
        this.name=name;  
    }  
    public void run(){  
        for(int i=1;i<=10;i++){  
            if(ticket>0){  
                System.out.println("线程"+name+"卖票"+(ticket--));  
                }  
        }  
    }  
}  
public class RunnableDemo02  
{  
    public static void main(String args[]){  
        MyThread A = new MyThread("A");  //实例化线程要执行的任务  
        Thread Ta = new Thread(A);    //实例两个线程对象,实际传递的是一个任务  
        Thread Tb = new Thread(A);    //因为两个线程执行的是一个任务,所以资源是共享的  
        Ta.start();  
        Tb.start();  
    }  
} 

解释:

因为一个线程只能启动一次,通过Thread实现线程时,线程和线程所要执行的任务是捆绑在一起的。也就使得一个任务只能启动一个线程,不同的线程执行的任务是不相同的,所以没有必要,也不能让两个线程共享彼此任务中的资源。

一个任务可以启动多个线程,通过Runnable方式实现的线程,实际是开辟一个线程,将任务传递进去,由此线程执行。可以实例化多个 Thread对象,将同一任务传递进去,也就是一个任务可以启动多个线程来执行它。这些线程执行的是同一个任务,所以他们的资源是共享。

两种不同的线程实现方式本身就决定了其是否能进行资源共享。

2、关于共享资源代码示例:用对Runnable接口的实现购票

 1 package com.asd.reserve.pojo.basic;
 2 
 3 /**
 4  * @author zs
 5  * @date 2019/9/20 12:00
 6  */
 7 public class SaleTicket implements Runnable{
 8 
 9     /*private int ticket=5;
10     @Override
11     public void run() {
12         for (int i = 0; i <100; i++) {//每个【窗口有100个人在排队
13 
14             sale();//调用本类中的同步方法sale()实现卖票
15 
16         }
17     }
18     public synchronized void sale(){//同步方法,加锁,给当前对象this加锁
19         //if开始
20         if (ticket>0) {
21             try {
22                 Thread.sleep(500);
23             } catch (InterruptedException e) {
24                 // TODO Auto-generated catch block
25                 e.printStackTrace();
26             }
27             System.out.println(Thread.currentThread().getName()+"卖第"+(ticket--)+"张票");
28 
29         }//if结束
30     }*/
31 
32 
33     private int ticket=5;
34     @Override
35     public void run() {
36         for (int i = 0; i <100; i++) {//每个【窗口有100个人在排队
37             //【1】同步代码块
38             synchronized (this) {
39                 //if开始
40                 if (ticket>0) {
41                     try {
42                         Thread.sleep(10);
43                     } catch (InterruptedException e) {
44                         // TODO Auto-generated catch block
45                         e.printStackTrace();
46                     }
47                     System.out.println(Thread.currentThread().getName()+"卖第"+(ticket--)+"张票");
48 
49                 }//if结束
50             }
51         }
52     }
53 }
54 
55 
56 
57 package com.asd.reserve.pojo.basic;
58 
59 /**
60  * @author zs
61  * @date 2019/9/20 12:03
62  */
63 public class SaleTicketTest {
64     public static void main(String[] args) {
65         //4个窗口
66         SaleTicket t1=new SaleTicket();
67         //【2】创建Thread类的对象
68         Thread t11=new Thread(t1,"我的线程1");
69         Thread t22=new Thread(t1,"我的线程2");
70         Thread t33=new Thread(t1,"我的线程3");
71         Thread t44=new Thread(t1,"我的线程4");
72 
73         //启动线程
74         t11.start();
75         t22.start();
76         t33.start();
77         t44.start();
78     }
79 }

解释:

共享资源(对象),如果不进行同步,线程就是不安全的。在本例中,若不对火车票进行同步,则可能多个线程同时通过了ticket > 0的
判断,但是在卖票时,可能卖的结果,被其他线程修改。出现下面的运行结果:

1 运行结果:
2 我的线程4卖第5张票
3 我的线程1卖第4张票
4 我的线程3卖第3张票
5 我的线程2卖第2张票
6 我的线程1卖第1张票
7 我的线程4卖第0张票
8 我的线程3卖第-1张票
9 我的线程2卖第-2张票

3、继承Thread实现多线程:无法实现资源共享

 1 package com.asd.reserve.pojo.basic;
 2 
 3 /**
 4  * @author zs
 5  * @date 2019/9/20 10:13
 6  */
 7 public class MyTicketThread extends  Thread {
 8     private int ticket=5;//仅剩五张票
 9     public MyTicketThread(String threadName) {
10         super(threadName);//调用父类的带参构造方法
11     }
12 
13     @Override
14     public void run() {
15 
16 
17         // TODO Auto-generated method stub
18         for(int i=0;i<100;i++){
19             synchronized (this) {
20                 if(ticket>0){
21                     try {
22                         Thread.sleep(300);
23                     } catch (InterruptedException e) {
24                         // TODO Auto-generated catch block
25                         e.printStackTrace();
26                     }
27                     System.out.println(Thread.currentThread().getName() + "卖第"
28                             + (ticket--) + "张票");
29                 }
30             }
31         }
32     }
33 }
34 
35 
36 package com.asd.reserve.pojo.basic;
37 
38 import java.util.Random;
39 import java.util.concurrent.LinkedBlockingQueue;
40 import java.util.concurrent.RejectedExecutionHandler;
41 import java.util.concurrent.ThreadPoolExecutor;
42 import java.util.concurrent.TimeUnit;
43 
44 /**
45  * @author zs
46  * @date 2019/9/20 10:14
47  */
48 public class Test {
49     public static void main(String[] args) {
50         //4个窗口
51         MyTicketThread t1=new MyTicketThread("A窗口");
52         MyTicketThread t2=new MyTicketThread("B窗口");
53         MyTicketThread t3=new MyTicketThread("C窗口");
54         MyTicketThread t4=new MyTicketThread("D窗口");
55         //启动线程
56         t1.start();
57         t2.start();
58         t3.start();
59         t4.start();
60     }
61 }

4、同步:提高线程的安全性,例如购票。

  • 同步:synchronized
  • 异步:asynchronized
  • 什么叫同步:完成一件事情,需要N多个操作步骤,这些步骤必须一次性执行完毕,才允许执行其它的操作。
  •  同步代码块:同步的可以是任意的对象。
 1 public class SaleTicket implements Runnable {
 2     private int ticket=5;
 3     @Override
 4     public void run() {
 5         for (int i = 0; i <100; i++) {//每个【窗口有100个人在排队
 6             //【1】同步代码块
 7             synchronized (this) {
 8                 //if开始
 9                 if (ticket>0) {
10                     try {
11                         Thread.sleep(500);
12                     } catch (InterruptedException e) {
13                         // TODO Auto-generated catch block
14                         e.printStackTrace();
15                     }
16                     System.out.println(Thread.currentThread().getName()+"卖第"+(ticket--)+"张票");
17                     
18                 }//if结束
19             }
20         }
21     }
22 }
  • 同步方法:只能锁当前对象this
 1 public class SaleTicket2 implements Runnable {
 2     private int ticket=5;
 3     @Override
 4     public void run() {
 5         for (int i = 0; i <100; i++) {//每个【窗口有100个人在排队
 6         
 7                 sale();//调用本类中的同步方法sale()实现卖票
 8             
 9         }
10     }
11     public synchronized void sale(){//同步方法,加锁,给当前对象this加锁
12         //if开始
13         if (ticket>0) {
14             try {
15                 Thread.sleep(500);
16             } catch (InterruptedException e) {
17                 // TODO Auto-generated catch block
18                 e.printStackTrace();
19             }
20             System.out.println(Thread.currentThread().getName()+"卖第"+(ticket--)+"张票");
21             
22         }//if结束
  • 对同步方法的应用
  1 /**
  2  * @author zs
  3  * @date 2019/11/11 15:16
  4  */
  5 public class Product {//商品
  6     //私有属性
  7     private String brand;//品牌
  8     private String name;//商品的名称
  9     private boolean flag=false;//赋初始值,为false,false认为没有商品,true代表有商品
 10     //公的取值,赋值方法
 11     public String getBrand() {
 12         return brand;
 13     }
 14     public void setBrand(String brand) {
 15         this.brand = brand;
 16     }
 17     public String getName() {
 18         return name;
 19     }
 20     public void setName(String name) {
 21         this.name = name;
 22     }
 23     //构造方法
 24     public Product(String brand, String name) {
 25         super();
 26         this.brand = brand;
 27         this.name = name;
 28     }
 29     public Product() {
 30         super();
 31     }
 32     /**生产者生产商品--》Product的对象的属性赋值--》赋品牌,赋商品名称*/
 33     public synchronized void set(String b,String n){//同步方法,锁当前对象,当前对象是this,this-->商品对象
 34         if(flag==true){//flag的值为false时就生产商品,true等待    ,相当于flag==true
 35             try {
 36                 super.wait();
 37             } catch (InterruptedException e) {
 38                 // TODO Auto-generated catch block
 39                 e.printStackTrace();
 40             }//父类Object中的方法  ,由notify()唤醒,唤醒后,将从wait()方法之后的代码开始执行
 41         }//if结束
 42         /**给属性赋值,*/
 43         this.setBrand(b);
 44         try {
 45             Thread.sleep(200);
 46         } catch (InterruptedException e) {
 47             // TODO Auto-generated catch block
 48             e.printStackTrace();
 49         }
 50         this.setName(n);
 51         System.out.println("生产者生产商品,品牌为:"+this.getBrand()+"------>商品名称为:"+this.getName());
 52         /**赋值结束,商品生产结束*/
 53         flag=true;
 54         super.notify();//唤醒消费者线程
 55 
 56     }
 57     /**消费者取走商品》this*/
 58     public synchronized void get(){
 59         if(flag==false){//flag为false是代表没有商品,没有商品,消费者等待
 60             try {
 61                 super.wait();
 62             } catch (InterruptedException e) {
 63                 // TODO Auto-generated catch block
 64                 e.printStackTrace();
 65             }
 66         }
 67         /**取走商品*/
 68         try {
 69             Thread.sleep(200);
 70         } catch (InterruptedException e) {
 71             // TODO Auto-generated catch block
 72             e.printStackTrace();
 73         }
 74         System.out.println("-------》消费者线程,取走了商品"+this.getBrand()+"-------"+this.getName());
 75         /**商品取走完成*/
 76         flag=false;
 77         super.notify();//唤醒生产者线程
 78     }
 79 }
 80 
 81 
 83 /**
 84  * @author zs
 85  * @date 2019/11/11 15:17
 86  */
 87 public class Customer implements Runnable{//消费者线程
 88     private Product pro;
 89     public Customer( Product pro){
 90         this.pro=pro;
 91     }
 92     @Override
 93     public void run() {
 94         for (int i = 0; i <10; i++) {
 95             //调用生产者中的同步方法,get
 96             pro.get();
 97         }
 98     }
 99 
100 }
101 
102 
103 /**
104  * @author zs
105  * @date 2019/11/11 15:17
106  */
107 public class ShengChanZhe implements Runnable{//生产者线程
108     private Product pro;//私有属性,共享资源商品
109     //构造方法
110     public ShengChanZhe(Product pro){
111         this.pro=pro;
112     }
113     @Override
114     public void run() {
115         //生产者,负责生产商品
116         for(int i=0;i<10;i++){
117             if(i%2==0){//偶数就生产矿泉水
118                 //调用商品类的同步的set方法
119                 pro.set("娃哈哈", "矿泉水");
120             }else{//奇数就生产小馒头
121                 pro.set("旺仔", "小馒头");
122             }
123 
124         }
125     }
126 
127 }
128 
129 
130 /**
131  * @author zs
132  * @date 2019/11/11 15:17
133  */
134 public class Test {
135     public static void main(String[] args) {
136         //创建共享资源的对象
137         Product pro=new Product();
138         //创建生产者的线程
139         ShengChanZhe sch=new ShengChanZhe(pro);
140         //创建消费者的线程
141         Customer cust=new Customer(pro);
142         //开始启动线程
143         new Thread(sch).start();
144         new Thread(cust).start();
145     }
146 }
小结:
【1】线程之间的通信
Object类中的方法 wait()等待 与notify()唤醒等待池中的一个线程
必须与同步代码块或同步方法一起使用

 
  • 关于同步方法与同步代码块的深入理解
    • 同步监视器:synchronized(obj){}中对的obj称为同步监视器。同步代码块中同步监视器可以是任何对象,但是推荐使用共享资源作为同步监视器;同步方法中无需指定同步监视器,因为同步方法的监视器是this,也就是该对象未身。
    • 同步监视器的执行过程:
      • 第一个线程访问,锁定同步监视器,执行其中代码 ;
      • 第二个线程访问,发现同步监视器被锁定,无法访问 ;
      • 第一个线程访问完毕,解锁同步监视器 ;
      • 第二个线程访问,发现同步监视器未锁,锁定并访问。
    • 同步方法:只能锁当前对象this;
    • 同步代码块:可以锁任何对象;
  • 关于同步的经验
    如果共享资源是一个基本数据类型,那么锁当前对象 (建议)同步方法;
    如果共享资源是一个引用数据类型,那么锁这个引用数据类型对象(建议)同步代码块;
    线程之间的通信必须与同步代码块或同步方法一起使用。

五、法术篇

常用方法  1 线程常用的方法

  2 【1】    获取当前线程对象static   currentThread()  Thread对象
  3 【2】    获取线程对象的名称getName()   String类型;
  4 public static void main(String[] args) {
  5         Thread t=Thread.currentThread();//获取当前正在运行的线程对象
  6         System.out.println(t.getName());//获取线程的名称
  7         
  8 }
  9 主线程的名称叫main
 10 
 11 currentThread()  到底是什么? 其实currentThread() 只是Thread 的一个静态方法。返回的正是执行当前代码指令的线程引用: 
 12 
 13     /**
 14      * Returns a reference to the currently executing thread object.
 15      *
 16      * @return  the currently executing thread.
 17      */
 18     public static native Thread currentThread();
 19 换句话说, Thread.currentThread() 返回的是一个实例。 只不过呢, 这个实例确实比较特殊。 这个实例是当前Thread 的引用。
20 21 【3】 获取或设置线程的优先级 22 public static void main(String[] args) { 23 Thread t=Thread.currentThread();//获取当前正在运行的线程对象 24 /**设置线程的优先级1-10*/ 25 t.setPriority(10);// 26 System.out.println(t.getPriority()); 27 System.out.println("最高的优先级"+Thread.MAX_PRIORITY);//10 28 System.out.println("最低的优先级:"+Thread.MIN_PRIORITY);//1 29 System.out.println("默认优先级:"+Thread.NORM_PRIORITY);//5 31 }
     32 33 【4】 线程是否处于活动状态isAlive(); 34 public static void main(String[] args) { 35 //【1】创建线程类的对象 36 MyRunnable my=new MyRunnable(); 37 //【2】创建Thread类的对象 38 Thread t=new Thread(my,"我的线程"); 39 System.out.println("启动之前,线程是否处于活动状态:"+t.isAlive());//false 40 //【3】启动线程 41 t.start(); 42 System.out.println("启动之后,线程是否处于活动状态:"+t.isAlive());//true 43 /***以下代码是主线程中的循环*/ 44 for (int i = 0; i <10; i++) { 45 System.out.println(Thread.currentThread().getName()+"=====>"+i); 46 } 47 /**以下代码,执行结果不确定 48 * [1]true:结果主线程 先结束,结果为true 49 * [2]false:自定义的线程,先结束,结果为 false*/ 50 System.out.println("自定义的线和是否处于活动状态:"+t.isAlive()); 51 } 52 53 54 【5】 线程的强制执行join(),当前线程强制执行,阻塞其它线程 55 public static void main(String[] args) throws InterruptedException { 56 //【1】创建线程类的对象 57 MyRunnable my=new MyRunnable(); 58 //【2】创建Thread类的对象 59 Thread t=new Thread(my,"我的线程"); 60 t.start(); 61 /***以下代码是主线程中的循环*/ 62 for (int i = 0; i <10; i++) { 63 if (i>4) { 64 t.join();//当前线程t强制执行,阻塞了主线程main 65 } 66 System.out.println(Thread.currentThread().getName()+"=====>"+i); 67 } 68 } 69 70 【6】 线程的休眠 sleep(毫秒) 71 public class MyRunnable2 implements Runnable { 72 @Override 73 public void run() { 74 try { 75 System.out.println("1.开始进入线程,准备休眠"); 76 Thread.sleep(3000); 77 System.out.println("2.休眠结束"); 78 } catch (InterruptedException e) { 79 // TODO Auto-generated catch block 80 e.printStackTrace(); 81 } 82 System.out.println("3.^_^............"); 83 } 84 } 85 86 public class TestSleep { 87 public static void main(String[] args) { 88 //创建线程类的对象 89 MyRunnable2 my=new MyRunnable2(); 90 91 new Thread(my).start();//启动线程 92 93 /***以下代码是主线程中的循环*/ 94 for (int i = 0; i <10; i++) { 95 System.out.println(Thread.currentThread().getName()+"=====>"+i); 96 } 97 } 98 } 99 100 【7】 线程的礼让 yield(),当前线程礼让一次 101 public static void main(String[] args) { 102 //【1】创建线程类的对象 103 MyRunnable my=new MyRunnable(); 104 //【2】创建Thread类的对象 105 Thread t=new Thread(my,"我的线程"); 106 t.start(); 107 108 /***以下代码是主线程中的循环*/ 109 for (int i = 0; i <10; i++) { 110 if (i==5) { 111 Thread.yield(); 112 System.out.println("当前线程礼让一次,当前线程是主线程"); 113 } 114 System.out.println(Thread.currentThread().getName()+"=====>"+i); 115 } 116 } 117 【8】还有许多其他的方法,不再进行介绍与了解,等到用的时候再学习,有兴趣得同学可以自己看API文档或自行百度。

总结

 1 总结:
 2 (1)获取当前线程对象
 3 Thread t=Thread.currentThread();//获取当前正在运行的线程对象
 4 System.out.println(t.getName());//获取线程的名称
 5 关于 Thread.currentThread()
 6 (2)线程是否处于活动状态isAlive();
 7 (3)线程的强制执行join(),当前线程强制执行,阻塞其它线程;
 8 (4)线程的休眠 sleep(毫秒):Thread.sleep(3000);
 9 (5)线程的礼让 yield(),当前线程礼让一次:Thread.yield();
10 (6)获取或设置线程的优先级:CPU会尽量将执行资源让给优先级比较高的线程。
11 Thread t=Thread.currentThread();//获取当前正在运行的线程对象
12 t.setPriority(10);//
13 System.out.println(t.getPriority());
14 System.out.println("最高的优级"+Thread.MAX_PRIORITY);//10
15 System.out.println("最低的优先级:"+Thread.MIN_PRIORITY);//1
16 System.out.println("默认优先级:"+Thread.NORM_PRIORITY);//5

五、提炼

 参看五月仓颉的博文有关于线程的详细总结。
 
 
 

推荐阅读