Java----多线程详解(一)
Java----多线程详解
多线程是Java基础知识的最后一部分,同样也是Java学习中的一个难点。在学习多线程时,深刻理解它的思想是非常必要的,在有了多线程的思想后,学习多线程的语法和规则就会很容易。
1、多线程简介
多线程是现代操作系统的发展方向,所以Java肯定是支持多线程的,它具有并发性、执行效率高的特点。多线程是实现并发的一种有效手段,一个进程可以通过运行多个线程来并发地执行多项任务,而多个线程之间的调度执行是由系统来实现的。
进程是一个执行的程序,多进程的多任务处理的特点是允许计算机同时运行两个或多个程序。它也是一次动态的执行过程,指的是从代码加载、执行到执行结束的一个完整过程。
线程是比进程小的执行单位,进程的每个小部分都由线程来管理。多线程程序比多进程程序需要更少的“管理费用”。线程共享相同的地址空间并且共同分享同一个进程,这可以使及其节省大量的CPU利用空间,从而能CPU能执行更多的任务。
下面先看一个使用多线程的示例程序:
public class test{
ThreadUseExtends tue = newThreadUseExtends();
public static void main(String[] str) {
//主线程
test t = new test();
t.tue.start();//启动线程
try {
Thread.sleep(1000);//主线程挂起1秒
} catch (Exception e) {
return;
}
}
public void PrintA() {
for (int i = 0; i < 10; i++) {
System.out.println(i +"");
}
}
public void PrintB() {
for (int i = 10; i < 20; i++) {
System.out.println(i +"");
}
}
class ThreadUseExtends extends Thread{
@Override
public void run() {
PrintA();
System.out.println();
try {
for (int i = 0; i < 10; i++){
sleep(1000);//线程挂起1秒
System.out.print("*");
}
System.out.println();
PrintB();
} catch (Exception e) {
System.err.println(e);
}
}
}
}
2、如何创建线程
线程的创建包括主线程的创建、实现Runnable接口创建和继承Thread类的创建。
1)、主线程的创建
程序的主线程在程序启动执行时立刻运行,它是所有的线程中最早运行的线程,还是产生其他子线程的线程,同时由于它要执行各种关闭动作,又是最后完成的线程。主线程是在程序启动时自动创建的,但它也是可以由Thread对象控制的。可以通过Thread对象调用currentThread()方法获得一个主线程的引用。获得主线程的引用后,就可以像控制其他线程那样控制主线程了。下面是一个示例:
public static voidmain(String[] str) {
//获得主线程的引用
Thread t = Thread.currentThread();
System.out.println("主线程:" + t);
t.setName("我的主线程");
System.out.println("改变名称后:" + t);
//线程内容
try {
for (int i = 0; i < 5; i++) {
System.out.println(i);
t.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
t作为语句println()中参数运用时输出的结果,它的顺序为:线程名称、优先级和组的名称。
2)、实现Runnable接口
创建一个线程有两个方法:实现Runnable接口;进程Thread类。创建线程最简单的方法就是创建一个实现Runnable接口的类,Runnable抽象了一个执行代码单元。可以通过实现Runnable接口的方法创建每一个对象的线程。实现Runnable接口的类,需要定义一个名为run()的无参数方法。
实现了Runnable类不需要是Thread的子类就可以运行,只需要实例化一个Thread对象,并把自己的引用当作参数传递给Thread的构造方法即可。
每个执行线程开始都作为Thread类的一个实例,无论是在哪种创建线程的方法中。首先需要实例化Runnable类:
MyRunnable r = newMyRunnable();
然后即可引用Runnable类实例化线程:
Thread t = new MyRunnable();
下面是一个通过实现Runnable接口来创建线程的实例:
public class test{
public static void main(String[] str) {
//实例化一个线程
MyThread r = new MyThread();
Thread t = new Thread(r);
t.setPriority(5);//设置线程的优先级
t.start();//对run方法进行调用
//主程序内容
try {
for (int i = 0; i < 10; i++) {
System.out.println("b");
Thread.sleep(1000);
}
} catch (Exception e) {
}
}
}
//通过实现Runnable接口创建线程
class MyThread implementsRunnable{
int i;
@Override
public void run() {
try {
while (true) {
System.out.println("a" + i++);
Thread.sleep(1000);
if (i == 10) break;
}
} catch (Exception e) {
}
}
}
请仔细研究这段代码,并多次调试观察打印出来的内容,你会有更好滴理解线程的概念和多线程执行的先后顺序。
3)、通过继承Thread类创建线程
Java中创建给管理线程的虚拟CPU是java.lang.Thread类的一个实例。也可以说Thread类的对象就是一个运行代码和使用数据的虚拟CPU。多个Thread对象可以共享代码和数据。继承Thread类创建线程的方法为:创建一个新类来继承Thread类,然后再创建该类的实例。当一个类继承Thread时,它必须重载run()方法,在这里run()方法也是新线程的入口。另外它也必须调用start()方法启动新线程执行。下面是一个实例:
public class test{
public static void main(String[] str) {
//实例化一个线程
MyThread r = new MyThread();
r.start();//对run()方法进行调用
r.setPriority(5);//设置优先级
try {
for (int i = 0; i < 10; i++) {
System.err.println("b");
Thread.sleep(1000);
}
}catch (Exception e) {
}
}
}
//通过继承Thread创建线程
class MyThread extends Thread{
int i;
@Override
public void run() {
try {
while (true) {
System.out.println("a" + i++);
Thread.sleep(1000);
if (i == 10) break;
}
} catch (Exception e) {
}
}
}
请将上面的实例跟通过实现Runnable接口创建线程的实例进行对比,通过代码对比和打印内容的对比,你将会发现这两种方法的区别以及各自的优缺点。
4)、两种方法的比较
Thread类定义了很多方法可以被它的子类重载,但是只有一个方法必须被重载,那就是run()方法。这个方法也是Runnable接口所必须的。因此如果不需要重载Thread的其他方法,选择实现Runnable接口创建线程的方法是最好的。
5)、创建多线程
前面值编写了单线程和双线程,下面来创建多线程。多线程可以为系统的内存分配做一个很好的处理,缓解内存的压力。下面是一个多线程的示例:
public class test{
public static void main(String[] str) {
//创建3个线程
MyThread1 t1 = new MyThread1("第1线程");
MyThread2 t2 = new MyThread2("第2线程");
MyThread3 t3 = new MyThread3("第3线程");
t1.start();
t2.start();
t3.start();
}
}
class MyThread1 extendsThread{
String name;
public MyThread1(String threadName) {
name = threadName;
}
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
System.out.println(name +":" + i);
Thread.sleep(1000);
}
} catch (Exception e) {
}
System.err.println(name + "退出");
}
}
class MyThread2 extendsThread{
String name;
public MyThread2(String threadName) {
name = threadName;
}
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
System.out.println(name +":" + i);
Thread.sleep(1000);
}
} catch (Exception e) {
}
System.err.println(name + "退出");
}
}
class MyThread3 extendsThread{
String name;
public MyThread3(String threadName) {
name = threadName;
}
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
System.out.println(name +":" + i);
Thread.sleep(1000);
}
} catch (Exception e) {
}
System.err.println(name + "退出");
}
}
如果你是个聪明的人,你肯定会问,为什么不只创建一个Thread类MyThread1,然后实例化3个MyThread1开始执行呢?这样做代码不就少了吗?这个问题涉及到线程的优先级和线程的并发执行,现在讲不容易理解,下面我会进行详细的讲解。
在程序中用到了线程的构造器,在前面的程序中没有直接给出,而是应用的默认构造器。线程中的构造器很多,有如下几种:
> Thread()
> Thread(Runnable target)
> Thread(Runnable target ,String name)
> Thread(String name)
> Thread(ThreadGroup group ,Runnable target)
> Thread(ThreadGroup , group ,Runnable target , String name)
> Thread(ThreadGroup group ,String name)
对线程进行操作时,其运行结果是不确定的,也就是说起运行结果有可能不一样。这就是之前我为什么让大家反复调试查看打印结果的原因。不过可以肯定的是每个线程都将启动,每个线程都运行完成。