继续上一讲内容,这一讲我们主要讲解一下进程的实现方式,注意以下程序运行在双核电脑上。
1. 多线程
1)一个进程可以包含一个或多个线程
2)一个程序实现多个代码同时交替运行就需要产生多个线程
3)CPU随机的抽出时间,让我们的程序一会做这件事情,一会做另外一件事情[看是否抢占到CPU的资源]
4)同其他大多数编程语言不同,Java内置支持多线程编程(multithreaded programming)。多线程程序包含两条或两条以上并发运行的部分,把程序中每个这样的部分都叫作一个线程(thread)。每个线程都有独立的执行路径,因此多线程是多任务处理的一种特殊形式。
5)多任务处理被所有的现代操作系统所支持。然而,多任务处理有两种截然不同的类型:基于进程的和基于线程的。
1.基于进程的多任务处理是更熟悉的形式。进程(process)本质上是一个执行的程序。因此基于进程的多任务处理的特点是允许你的计算机同时运行两个或更多的程序。举例来说,基于进程的多任务处理使你在运用文本编辑器的时候可以同时运行Java编译器。在基于进程的多任务处理中,程序是调度程序所分派的最小代码单位。
2.而在基于线程(thread-based)的多任务处理环境中,线程是最小的执行单位。这意味着一个程序可以同时执行两个或者多个任务的功能。例如,一个文本编辑器可以在打印的同时格式化文本。
2.Processvs Thread
1)多线程程序比多进程程序需要更少的管理费用。进程是重量级的任务,需要分配给它们独立的地址空间。进程间通信是昂贵和受限的。进程间的转换也是很需要花费的。另一方面,线程是轻量级的选手。它们共享相同的地址空间并且共同分享同一个进程。线程间通信是便宜的,线程间的转换也是低成本的。
2) 多线程可帮助你编写出CPU最大利用率的高效程序,使得空闲时间保持最低。这对Java运行的交互式的网络互连环境是至关重要的,因为空闲时间是公共的。例如,网络的数据传输速率远低于计算机处理能力,而本地文件系统资源的读写速度也远低于CPU的处理能力。当然,用户输入也比计算机慢很多。在传统的单线程环境中,程序必须等待每一个这样的任务完成以后才能执行下一步—尽管CPU有很多空闲时间。多线程使你能够获得并充分利用这些空闲时间。
3.线程的实现
1)在Java中通过run方法为线程指明要完成的任务,有两种技术来为线程提供run方法。
(1)继承Thread类并重写run方法。
(2)通过定义实现Runnable接口的类进而实现run方法。
2)继承Thread类并重载run方法。
Thread类:是专门用来创建线程和对线程进行操作的类。Thread中定义了许多方法对线程进行操作。
Thread类在缺省情况下run方法什么都不做。可以通过继承Thread类并重写Thread类的run方法实现用户线程。查看JDK Doc文档Thread类,其中有一个方法是run()和start()方法
public void run()
If this thread was constructed using a separate Runnable run object, then that Runnable object's run method is called; otherwise, this method does nothing and returns.
Subclasses of Thread should override this method.
[如果这个线程被使用一个单独的Runnable对象构造,然后这个Runnable 对象的run()方法就会被调用,否则,这个方法不做任何事情,并且返回,继承Thread的类必须重写这个方法]
[run()方法有什么作用呢?当我们启动一个线程,肯定要让线程帮助我们做一些事情,做这些事情的代码就是写run()中,那如何去调用这个方法呢?注意我们必须使用start()方法去调用这个run()方法来启动一个线程,start()是启动一个线程的唯一方法]
查看start()方法:
public void start()
Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread.It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution.
[导致这个线程开始执行,Java虚拟机会调用这个线程的run()方法。一个线程不能启动超过一次,尤其,一个线程不能重新启动一旦它执行完毕之后,换句话线程执行完之后是不能重新启动的,必须要再开启一个线程]
所以我们要把代码写在run()方法里面,然后调用start()方法,这样启动一个线程,start()方法调用run(),这样我们代码就会执行了。注意千万不要重写start()方法,我们要用父类的这个start()方法
package com.ahuier.Thread; public class ThreadTest { public static void main(String[] args) { Thread1 t1 = new Thread1(); Thread2 t2 = new Thread2(); /* * 注意必须用strart()来启动一个线程 * 它完成一些事情:1.线程启动分配好一些资源 2.调用run()方法 * 注意线程一旦启动之后就不受我们控制而转给CPU */ t1.start(); t2.start(); } } class Thread1 extends Thread{ @Override public void run() { for(int i = 0; i < 100; i++){ System.out.println("hello world:" + i); } } } class Thread2 extends Thread{ @Override public void run() { for(int i = 0; i < 100; i++){ System.out.println("welcome" + i); } } }编译执行结果可以发现是输出顺序是乱的,说明两个是受CPU控制,谁此时抢到CPU资源,谁就去执行【说明】:此时如果main代码改为如下所示:
Thread1 t1 = new Thread1(); Thread1 t2 = new Thread1(); /* * 注意必须用strart()来启动一个线程 * 它完成一些事情:1.线程启动分配好一些资源 2.调用run()方法 * 注意线程一旦启动之后就不受我们控制而转给CPU */ t1.start(); t2.start();编译执行结果可以发现,顺序也是乱的,这边生成一个线程的两个对象,本质上一样的【说明】:
1) 一个进程至少要包含一个线程。
2) 对于单核 CPU 来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来 看,多个线程在同时执行(宏观并行)。
3) 对于双核或双核以上的 CPU 来说,可以真正做到微观并行
3)实现Runnable接口的类实现run方法。
通过建立一个实现了Runnable接口的类,并以它作为线程的目标对象来创建一个线程。
Runnable接口:定义了一个抽象方法run()。定义如下:
public interface java.lang.Runnable{
public abstract void run();
}
查看JDK Doc文档的Runable接口,这个接口里面只有一个run()方法
public interface Runnable
The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread. The class must define a method of no arguments called run.
[如果一个类想成为线程类,则这个类必须去实现Runnable接口,这个类必须定义一个不带参数的run()run方法]
void run()
When an object implementing interface Runnable is used to create a thread, starting the thread causes the object's run method to be called in that separately executing thread.The general contract of the method
run
is that it may take any action whatsoever.[当一个对象实现了Runnable接口的话它用于创建一个线程,调用这个对象的run()方法会启动一个线程,在这个单独的线程上,你可以把你想要实现的代码放在run()方法中。]
package com.ahuier.thread; public class ThreadTest2 { public static void main(String[] args) { /* * 比较JDK Doc文档Thread类的构造方法 * 查看public Thread(Runnable target),它接受一个实现Runable接口的实例 * 使用匿名内部类 */ Thread t1 = new Thread(new Runnable(){ @Override public void run() { for(int i = 0; i < 100; i++){ System.out.println("hello :" + i); } } }); t1.start(); } }编译执行结果输出0 到 99.
package com.ahuier.thread; public class ThreadTest2 { public static void main(String[] args) { Thread t1 = new Thread(new MyThread()); t1.start(); Thread t2 = new Thread(new MyThread2()); t2.start(); } } class MyThread implements Runnable{ @Override public void run() { for(int i = 0; i < 100; i++){ System.out.println("hello :" + i); } } } class MyThread2 implements Runnable{ @Override public void run() { for(int i = 0; i < 100; i++){ System.out.println("welcome: " + i); } } }编译执行结果不贴出来了,输出顺序是乱的,说明两个是受CPU控制,谁此时抢到CPU资源,谁就去执行
4.总结
5. 查看JDK Thread类的stop()方法1)两种方法均需执行线程的start方法为线程分配必须的系统资源、调度线程运行并执行线程的run方法。
2)在具体应用中,采用哪种方法来构造线程体要视情况而定。通常,当一个线程已继承了另一个类时,就应该用第二种方法来构造,即实现Runnable接口。
3)线程的消亡不能通过调用一个stop()命令。而是让run()方法自然结束。
@Deprecated
public final void stop()
Deprecated. This method is inherently unsafe. Stopping a thread with Thread.stop causes it to unlock all of the monitors that it has locked (as a natural consequence of the unchecked ThreadDeath exception propagating up the stack).
[可以发现这种方法是不安全的,所以这个这个方法我们是不建议使用的。]
6. 那么如何让线程停止呢?我们可以用如下的使用方式,这也是停止线程,推荐的使用方式。
public class MyThread implements Runnable{
private boolean flag=true;
public void run(){
while (flag){
…}
}
public void stopRunning(){flag=false;}
}
public class ControlThread{private Runnable r=new MyThread();
private Thread t=new Thread(r);
public void startThread(){t.start(); }
publi void stopThread(){r.stopRunning();}
}