当前位置:K88软件开发文章中心编程语言JavaJava01 → 文章内容

复习一下Java多线程

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-4 7:54:48

-->

什么是进程?

狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。
广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
线程: 线程是比进程更小的执行单位,一个进程在执行过程中可以产生多个线程,形成多条执行线索,每条线索,即每个线程也有它自身的产生存在消亡的过程。和进程可以共享操作系统的资源类似,线程也可以共享进程中的某些内存单元(包括代码数据。),并利用这些共享单元来实现实现数据交换,实时通信与必要的同步操作。但与进程不同的是,线程的中断与恢复可以更加节省系统的开销,具有多个线程的进程能够更好的表达和解决现实世界的具体问题。

java多线程机制

Java的一大特点就是内置多线程的支持。多线程是指一个应用程序中同时存在几个执行体,按几条不同的执行线索共同工作的情况。这样就能很方便的开发出具有多线程功能,能同时处理多个任务的功能强大的应用程序。

主线程(main线程)

当JVM加载代码,发现main方法之后,就会启动一个线程,这个线程就被称为“主线程(main线程)”,该线程负责执行main方法。如果main方法没有创建其他线程,那么当main方法执行完最后一个语句,即main方法返回时,JVM就会结束我们的java应用程序。如果main方法又创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,保证每个线程都有机会获得CPU资源,main方法即使执行完最后的语句(主线程结束),JVM也不会结束java应用程序,一直会等到java应用程序中所有的线程都结束以后才结束应用程序。

线程的状态与生命周期

1.新建

当一个Thread类其子类的对象被声明并创建时,新生的线程对象处于新建状态。此时他已经有了相应的内存空间其他资源

2.运行

线程创建后仅仅占有的是内存资源,在JVM管理的线程中还没有这个线程,此线程必须调用start()方法,(从父类继承的方法)通知JVM,这样,JVM就会知道又有一个新线程排队等候切换了。

当JVM把使用权交给线程时,如果线程是Thread类的子类创建的,该类中的run()方法就立刻执行。run()方法规定了该线程的具体使命。所以程序必须在子类中重写父类的run方法。在线程没有结束run方法之前,不要让线程再调用start()方法,否则会发生IllegalThreadStateException异常。

3.中断

有4中原因的中断:

  • JVM将CPU资源从当前线程切换给其他线程,使本线程让出CPU的使用权处于中断状态。
  • 线程使用了CPU资源期间,执行了sleep(int millsecond)方法,使当前线程进入休眠状态。sleep(int millsecond)方法是Thread类的一个类方法。线程一旦执行了sleep(int millsecond)方法,就立刻让出cpu的使用权,使当前线程处于中断状态。经过参数millsecond指定的毫秒数之后,该线程就重新进到线程队列中排队等待CPU资源,以便从中断出继续运行。
  • 线程使用cpu资源期间,执行了wait()方法,使得当前线程进入等待状态,必须有其他线程调用notify()方法通知它,使得它重新进入到线程队列中排队等待CPU资源,以便从中断处继续运行。
  • 线程使用CPU资源期间,执行某个操作进入了阻塞状态,不能进入排队队列。

4.死亡

处于死亡状态的线程不具有继续运行的能力。线程死亡的原因有两种。

一:正常运行的线程执行完毕,结束了run()方法。

二:线程被提前强制性的终止,即强制run()方法结束。

所谓死亡状态就是线程释放了实体,即释放了分配给线程对象的内存。

多线程实现键盘练习游戏:

ThreadDemo.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package cn.k88;

public class ThreadDemo {

    public static void main(String[] args) {
        System.out.println("键盘练习游戏(输入#结束程序)");
        System.out.println("输入显示的字母(回车)\n");
        Letter letter;
        letter =new Letter();
        GiveLetterThread GiveChar;
        InputLetterThread typeChar;
       
        GiveChar =new GiveLetterThread();
        GiveChar.setLetter(letter);
        GiveChar.setSleepLength(3200);
       
        typeChar =new InputLetterThread();
        typeChar.setLetter(letter);
        GiveChar.start();
        typeChar.start();

    }

}

Letter.java

1
2
3
4
5
6
7
8
9
10
11
12
package cn.k88;

public class Letter {
    char c='\0';
    public void setChar(char c){
        this.c=c;
    }
    public char getChar(){
        return c;
    }

}

GiveLetterThread.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package cn.k88;

public class GiveLetterThread extends Thread{
    Letter letter;
    char startChar='a',endChar='z';
    int sleepLength=5000;
    public void setLetter(Letter letter){
        this.letter=letter;
    }
   
    public void setSleepLength(int n){
        sleepLength=n;
    }
    public void run(){
        char c=startChar;
        while(true){
            letter.setChar(c);
            System.out.printf("显示的字符:%c\n",letter.getChar());
            try{
                Thread.sleep(sleepLength);
            }
            catch(InterruptedException e){
               
            }
           
            c=(char)(c+1);
            if(c>endChar)
                c=startChar;
        }
    }
   

}

InputLetterThread.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package cn.k88;

import java.util.Scanner;

public class InputLetterThread extends Thread{
    Scanner reader;
    Letter letter;
    int score=0;
     InputLetterThread() {
        reader=new Scanner(System.in);
       
    }
     public void setLetter(Letter letter){
         this.letter=letter;
     }
     
     public void run(){
         while(true){
             String str=reader.nextLine();
             char c=str.charAt(0);
             if(c==letter.getChar()){
                 score++;
                 System.out.printf("\t\t输入对了,目前分数%d\n",score);
                 
             }
             else
             {
                 System.out.printf("\t\t输入错误,目前分数%d\n",score);
             }
             if(c=='#')
                 System.exit(0);
         }
         
         
     }

}

线程调度与优先级

处于就绪状态的线程首先进入就绪队列排队等候CPU资源,同一时刻在就绪队列中的线程可能有多个。Java虚拟机中的线程调度器负责管理线程,调度器把线程的优先级分为10个级别。分别用Thread类中的类常量表示。每个线程的优先级都在常数1和10之间,即Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之间。如果没有明确地设置线程的优先级别,每个线程的优先级都为常数5,即Thread.NORM_PRIORITY线程的优先级可以通过setPriority(int grade)方法调整。如果参数不在1~10范围内,那么setPriority便产生一个IllegalArgumenException异常。

JVM的线程调度器的任务是使高优先级的线程能始终运行,一旦时间片有空闲,则使具有同等优先级的线程以轮流的方式顺序使用时间片。
在实际编程时,不提倡使用线程的优先级来保证算法的正确执行。

Thread类与线程的创建

使用Thread的子类

在Thread的子类创建对象时,需要重写父类的run()方法,其目的是规定线程的具体操作。

使用Thread类

使用Thread创建线程通常使用的构造方法是:Thread(Runnable target)。该构造方法中的参数是一个Runnable类型的接口。

使用Thread类创建线程实例:

ThreadDemo1.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package cn.k88;

public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread speakElephant;      //声明线程
        Thread speakCar;           //声明线程
       
        ElephantTarget elephant=new ElephantTarget();  //实例化实现runnable接口的类
        CarTarget car=new CarTarget();              
       
        speakElephant=new Thread(elephant);  //创建线程
        speakCar=new Thread(car);           //创建线程
       
        speakElephant.start();   //启动线程
        speakCar.start();       //启动线程
    }

}

ElephantTrget.java

1
2
3
4
5
6
7
8
9
10
11
12
package cn.k88;

public class ElephantTarget implements Runnable{

    public void run() {
        for(int i=1;i<=20;i++){
            System.out.println("大象"+i+" ");
        }
       
    }

}

CarTarget.java

1
2
3
4
5
6
7
8
9
10
11
package cn.k88;

public class CarTarget implements Runnable{
    public void run(){
        for(int i=1;i<=20;i++){
            System.out.println("轿车"+i+" ");
        }
       
    }

}

线程的常用方法

start()

线程调用该方法将启动线程,使之从新建状态进入到就绪队列排序。

run()

Thread类的run()方法与Runnable接口中的run()方法的功能和作用相同。该方法由系统自动调用。

sleep(int millsecond)

线程可以在它的run()方法中调用sleep方法来使自己放弃CPU资源,休眠一段时间。如果线程在休眠时被打断,则JVM就抛出InterruptedException异常。

isAlive()

当一个线程调用start()方法,并占有CPU资源后,该线程的run()方法在运行过程中,我们调用isAlive()方法时返回true,线程进入死亡状态时,则返回false

currentThread()

currentThread()方法是Thread类中的类方法,可以用类名调用。该方法返回当前正在使用的CPU资源的线程。

interrupt()

当一些县城调用sleep()方法处于休眠状态时,一个占有CPU资源的线程可以让休眠的线程调用interrupt()方法“吵醒”自己。即导致休眠的线程发生InterruptException异常,从而结束休眠,重新排队等待CPU资源。

线程同步


复习一下Java多线程