JavaSE入门学习50:多线程编程

Posted life is wonderful

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaSE入门学习50:多线程编程相关的知识,希望对你有一定的参考价值。

        一进程和线程

        (1)进程和线程的基本概念

        进程:程序(任务)的执行过程;持有资源(共享内存,共享文件)和线程。

        线程:是一个程序内部的顺序控制流。

        我们生活中的进程例子很多,一个进程就相当于一个你在电脑上启动的程序。比如我们打开最常用的聊天工具

QQ,也可以打开Java的开发工具Eclipse等等。这些都算Window操作系统上的一个进程。

        对于Window操作系统上一个线程,还是拿QQ进程和Eclipse进程来举例,QQ进程中的文字聊天和收发文件就是

其中的两个线程;Eclipse进程中的源代码文件编辑、源代码编译和语法校验就是其中的三个线程。

        通过上面的知识可以得出:                     

        1)一个进程可以有多个线程;

        2)线程是系统中最小的执行单元;

        3)同一进程中有多个线程;

        4)线程共享进程的资源。

        (2)线程和进程的区别

        每个进程都有独立的代码和数据空间(进行上下文),进程间的切换会有较大的开销。多进程指的是在操作系统中

同时运行多个任务(程序)。

       线程可以看成轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线

程切换的开销小。多线程指的是在同一程序中有多个顺序流相同时执行。

       (3)线程交互

       线程间的通信包括互斥和同步。

       互斥:通常表现在多个线程竞争同一资源,比如这里只有一只笔,同时有几个人要进行签到,但资源(笔)唯一,

线程(人)之间就存在竞争关系,并且同一时间只有一个线程(人)能够持有这个资(笔)。

       同步:同步就是协同步调,按预定的先后次序进行运行,如进程、线程同步,可理解为进程或线程A和B一块配

合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。

       二Java多线程

       Java多线程是通过java.lang.Thread类来实现的。VM启动时会有一个由主方法(public static void main(){})所定义

的线程。可以通过创建Thread类的实例来创建新的线程。每个线程都是通过某个特定Thread类的对象所对应的方法

run()来完成其操作的,方法run()称为线程体。通过调用Thread类的start()方法来启动一个线程。

       (1)Thread类

       API中定义的Thread类:

       线程是程序中的执行线程。Java虚拟机允许应用程序并发地运行多个执行线程。 每个线程都有一个优先级,高优

先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程中运行的代码创建

一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,

新线程才是守护程序。 

       当Java虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main()方法。Java虚拟机会继续

执行线程,直到下列任一情况出现时为止: 

        1)调用了Runtime类的exit()方法,并且安全管理器允许退出操作发生。 

        2)非守护线程的所有线程都已停止运行,无论是通过从对run()方法的调用中返回,还是通过抛出一个传播到run()

方法之外的异常。 

       创建新执行线程有两种方法。一种方法是将类声明为Thread类的子类。该子类应重写Thread类的run()方法。接

下来可以分配并启动该子类的实例。例如,计算大于某一规定值的质数的线程可以写成:

class PrimeThread extends Thread {
       long minPrime;
       PrimeThread(long minPrime) {
           this.minPrime = minPrime;
       }

       public void run() {
           //compute primes larger than minPrime
            . . .
       }
}

        然后,下列代码会创建并启动一个线程: 

 PrimeThread p = new PrimeThread(143);
 p.start();

        创建线程的另一种方法是声明实Runnable接口的类。该类然后实现run()方法。然后可以分配该类的实例,在创

Thread类时作为一个参数来传递并启动。采用这种风格的同一个例子如下所示: 

class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
         }
 
         public void run() {
             // compute primes larger than minPrime
              . . .
         }
}

        然后,下列代码会创建并启动一个线程: 

PrimeRun p = new PrimeRun(143);
new Thread(p).start();

       每个线程都有一个标识名,多个线程可以同名。如果线程创建时没有指定标识名,就会为其生成一个新名称。 

       Thread类中定义关于线程优先级的字段:


       Thread类中定义的构造方法:


       Thread类中定义的方法:






       (2)Runnable接口

       API中描述的Runnable接口:

       Runnable接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为run()的无参数方法。

 设计Runnable接口的目的是为希望在活动时执行代码的对象提供一个公共协议。例如,Thread类实现了Runnable接

口。激活的意思是说某个线程已启动并且尚未停止。 

       此外,Runnable接口为非Thread子类的类提供了一种激活方式。通过实例化某个 Thread类实例并将自身作为运

行目标,就可以运行实现Runnable接口的类而无需创建Thread类的子类。大多数情况下,如果只想重写run()方法,

而不重写其他Thread类方法,那么应使用Runnable接口。这很重要,因为除非程序员打算修改或增强类的基本行

为,否则不应为该类创建子类。

       Runnable接口中只定义了一个方法:

 

       (3)线程的创建与启动

       Java中可以有两种方式创建新的线程。

       第一种:定义线程类实现Runnable接口

       Thread myThread=new Thread(target)  //target为Runnable接口类型。

       Runnable接口中只有一个方法:

       public void run();//定义线程运行体。

       使用Runnable接口可以为多个线程提供共享的数据。

       在实现Runnable接口的类的run()方法中可以使用Thread的静态方法:

       public static Thread correntThread()  获取当前线程的引用

       第二种:定义线程类继承Thread类

       定义一个Thread类的子类并重写其run()方法如:

       class MyThread extends Thread{

              public void run(){...}

        }

        然后生成该类的对象:

        MyThread myThread = new Mythread(...)

        实例:

        TestThread1.java源代码:

        方法调用的顺序执行,也就是先执行Runnable1线程,再执行主线程。

public class TestThread1 {
	public static void main(String args[]) {
		<span style="color:#ff6600;">Runner1 r = new Runner1();
		r.run();</span>
		
		for(int i=0; i<10; i++) {
			System.out.println("主线程:------" + i);
		}
	}
}

class Runner1 implements Runnable {
	public void run() {
		for(int i=0; i<10; i++) {	
			System.out.println("Runner1线程:" + i);
		}
	}
}
         运行结果:


        线程启动后的交替执行,其中最重要的是Thread类的start()方法,即Runnable1线程和主线程交替执行。

public class TestThread1 {
	public static void main(String args[]) {
		<span style="color:#ff6600;">Runner1 r = new Runner1();
		Thread t = new Thread(r);
		t.start();</span>
		
		for(int i=0; i<10; i++) {
			System.out.println("主线程:------" + i);
		}
	}
}

class Runner1 implements Runnable {
	public void run() {
		for(int i=0; i<10; i++) {	
			System.out.println("Runner1线程:" + i);
		}
	}
}

        运行结果:


        使用继承Thread类的修改后的代码:

public class TestThread1 {
	public static void main(String args[]) {
		<span style="color:#ff6600;">Runner1 r = new Runner1();
		r.start();</span>
		
		for(int i=0; i<10; i++) {
			System.out.println("主线程:------" + i);
		}
	}
}


<span style="color:#ff6600;">class Runner1 extends Thread</span> {
	public void run() {
		for(int i=0; i<10; i++) {	
			System.out.println("Runner1线程:" + i);
		}
	}
}

        运行结果:


       推荐使用实现Runnable接口的线程类来进行多线程的创建与启动,比较灵活。

       关于两种方法创建线程的不同

       第一:创建线程只有继承Thread类和实现Runnable接口两种方式(Thread也是实现了Runnable接口);

       第二:若以继承Thread类方式创建线程则这个类的实例就可以重写Thread类的几十个方法,注意是好几十个方

法;
       第三:若以实现Runnable接口的方式创建线程则仅仅只有一个run()方法可供重写(注意是只有一个);

       实现Runnable接口创建的线程最终还是要通过将自身实例作为参数传递给Thread类然后执行,当你要为一个没

有继承Thread类的类创建线程,而且只想让这个类的实例调用run()方法执行任务,这时候Runnable接口就相当于为

你的目标类提供了一个激活方式,设计该接口的目的是为了那些希望在激活时执行代码的对象提供一个公共协议,激

活的意思是说某个线程已启动并且尚未停止。 

       这两种创建线程的方法本身没有什么不同,一个是实现Runnable接口,一个是继承Thread类。而使用实现

Runnable接口这种方法:1.可以避免Java的单继承的特性带来的局限性;2.适合多个相同程序的代码去处理同一个资

源的情况,把线程同程序的代码及数据有效分离,较好地体现了面向对象的设计思想。开发中大多情况下都使用实现

Runnable接口这种方法创建线程。



以上是关于JavaSE入门学习50:多线程编程的主要内容,如果未能解决你的问题,请参考以下文章

JavaSE-09 Thread 多线程

JavaSE-09 Thread 多线程(完整版)

JavaSE多线程01 2021.05.26-27

JavaSE之多线程

多线程学习系列-初探深入浅出Java多线程-入门

JavaSE基础---多线程