Java给多线程编程提供了内置的支持。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
多线程是多任务的一种特别的形式,但多线程使用了更加小的资源开销。
这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。
一个线程的生命周期
- 新建状态
- 就绪状态
- 运行状态
- 阻塞状态
- 死亡状态
线程的优先级
每一个Java线程都有一个优先级,这样有助于操作系统确定线程的调度顺序
Java线程的优先级是一个整数,取值范围是1(Thread.MIN_PRIORITY)——
10(Thread.MAX_PRIORITY)
默认情况下,每一个线程都会分配一个优先级NORM_PRIORITY(5)
具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。
创建一个线程
通过implements Runnable接口来创建线程
package com.bamzhy.thread;
public class RunnableDemo implements Runnable {
private Thread t;
private String threadName;
//构造函数
RunnableDemo(String name){
threadName=name;
System.out.println("正在创造"+threadName);
}
/**
* 用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。
* 通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法。
* 这里方法run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
*/
@Override
public void run() {
System.out.println("正在运行"+threadName);
try{
for (int i = 0; i < 4 ; i++) {
System.out.println("这是 " + threadName + " " + i);
//让线程睡眠
Thread.sleep(50);
}
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println( threadName + " 被打断了.");
}
System.out.println( threadName + " 正在退出.");
}
//只有调用了start方法以后线程才会运行
public void start(){
System.out.println("正在开启"+threadName);
if (t==null){
t=new Thread(this,threadName);
System.out.println(threadName+"开启成功");
t.start();
}
}
}
这个是结果:
正在创造线程1
正在开启线程1
正在创造线程2
正在开启线程2
正在运行线程1
这是 线程1 0
正在运行线程2
这是 线程2 0
这是 线程1 1
这是 线程2 1
这是 线程1 2
这是 线程2 2
这是 线程1 3
这是 线程2 3
线程1 正在退出.
线程2 正在退出.
通过extends Thead类来创建线程
创建一个线程的第二种方法是创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。
继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。
该方法尽管被列为一种多线程实现方式,但是本质上也是实现了 Runnable 接口的一个实例。
代码基本相同
Run方法和Start方法的区别
start()方法被用来启动新创建的线程,而且start()内部 调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启 动,start()方法才会启动新线程
Thread的主要方法
- public void start()
使用该线程开始执行;Java虚拟机调用该线程的run方法 - public void run()
如果该线程是使用独立的Runnable运行对象构造的,则调用该Runnable对象的run方法,否则该方法不执行任何操作并返回 - public final void setName(String name)
改变线程名称,使之与参数name相同 - public final void setPriority(int priority)
更改线程的优先级 - public final void setDaemon(boolean on)
将该线程标记为守护线程或者用户线程 - public final void join(long milisec)
等待该线程终止的时间最长为millis毫秒 - public void interrupt()
中断线程 - public final boolean isAlive()
测试线程是否处于活动状态
下面的方法是thread类的静态方法
- public static void yield()
暂停当前正在执行的线程对象,并且执行其他线程
- public static void sleep(long millisec)
在指定的毫秒数内让正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响
- public static Thread cuurentThread()
返回对当前正在执行的线程对象的引用
- public static void dumpStack()
将当前线程的堆栈跟踪打印至标准错误流
- public static boolean holdsLock(Object x)
当且仅当当前线程在指定的对象上保持监视器锁时,才返回true
通过Callable和Future来创建线程
1.创建Callable接口的实现类,并且实现call方法,该call方法将作为线程执行体,并且有返回值
2.创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值
3.使用FutureTask对象作为Thread对象的target创建并且启动线程
4.调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
这个方法我没什么兴趣,有空再学
创建线程的三种方法的对比
采用实现 Runnable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口还可以继承其他类。
使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
线程的几个主要概念
在多线程编程时,你需要了解以下几个概念:
线程同步
线程间通信
线程死锁
线程控制:挂起、停止和恢复