1. 相关概念:
- 程序:安装在磁盘上的一段指令的集合,是静态的。
- 进程:是运行中的程序,是动态的。每个进程有独立的资源空间。
- 线程:又称轻量级进程,是程序执行流的最小单元,是程序中单一的顺序控制流程。线程是进程中的一个实体,是被系统独立调度和分派的基本单位。
- 多线程:在单个程序中,可以同时运行多个不同的线程执行不同的任务
线程才是真正干活的,进程无非是分配了资源的空间,一个进程中有多个线程,线程共享了进程的资源空间。所以当一个程序开始运行,至少已经开启了一个进程。
2. 线程模型:
2.1.通过继承Thread类创建线程
- 普通Java类如果继承了Thread类就成了一个线程类,并可以通过该类的start方法启动线程,执行线程代码。
- Thread类的子类可以直接实例化,但是在子类中必须覆盖run方法才能真正运行线程代码
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
//在子类中重写了thread类的run方法
public void run() {
// compute primes larger than minPrime
. . .
}
}
//实例化线程类的子类
PrimeThread p = new PrimeThread(143);
p.start();
2.2 通过实现Runnable接口创建线程
实现Runnable接口的类必须借助Thread类才能创建线程。通过Runnable接口创建线程分为两步;
- 创建实现Runnable接口的类的实例;
- 创建一个Thread类对象,将第一步实例化得到的Runnable对象作为参数传入Thread类的构造方法;
- 通过Thread类的start方法启动线程
//创建实现Runnable接口的类的实例
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);
//将实例化得到的Runnable对象作为参数传入Thread类的构造方法;
new Thread(p).start();
API:
http://tool.oschina.net/apidocs/apidoc?api=jdk-zh
3. Thread类常用方法:
static Thread currenThread():返回对当前正在执行的线程对象的引用。
long getId():返回该线程的标识符。
String getName():返回该线程的名称。
int getPriority(): 返回线程的优先级。
Thread.State getState():返回该线程的状态。
void interrupt():中断线程。
boolean isAlive():测试线程是否处于活动状态。
void join():等待该线程终止
static void sleep(long millis):线程休眠
void start():使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
4. Code Demo:
4.1 继承Thread类创建多个线程
public class ThreadDemo {
public static void main(String[] args) {
//产生一个线程类对象
HelloThread h1=new HelloThread();
//自定义线程名称setName
h1.setName("线程1");
//调用线程类实例的start方法,启动线程,就会调用线程实例的run方法执行
h1.start();
//一个线程类可以创建多个线程实例
//Thread(String name)方法在实例化线程的同时指定了线程名称
HelloThread h2=new HelloThread("线程2");
h2.start();
}
}
class HelloThread extends Thread{
//线程初始化
public HelloThread() {
}
//带参数的线程初始化
public HelloThread(String name) {
super(name);
}
//重写父类的run方法
@Override
public void run() {
for(int i=0;i<5;i++){
//获取线程的名称getName
System.out.println(this.getName()+"--"+i);
}
}
}
4.2 实现Runnable接口创建多个线程
public class HelloRunnableDemo {
public static void main(String[] args) {
HelloRunnable hr=new HelloRunnable();
Thread t1=new Thread(hr,"线程A");
t1.start();
Thread t2=new Thread(hr,"线程B");
t2.start();
}
}
class HelloRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<5;i++){
//获取线程的名称getName
System.out.println(Thread.currentThread().getName()+i);
}
}
}
一般情况下,使用第二种方法,即实现Runnable接口来创建线程,主要是因为:
- 避免单继承的局限,一个类可以实现多个接口,但只能继承一个类
- 适合资源的共享
我们用一个多线程买票的例子说明二者的区别:
用继承Thread的方法创建两个线程对象,s1和s2,各自不能共享数据,实际上是每个线程都有5张票。类似于“计划经济”,每个线程有固定的指标,彼此互不影响。
public class SharedDataThreadDemo {
public static void main(String[] args) {
TicketThread s1=new TicketThread("窗口1:");
s1.start();
TicketThread s2=new TicketThread("窗口2:");
s2.start();
}
}
class TicketThread extends Thread{
private int tickets=5;
public TicketThread(String name){
super(name);
}
@Override
public void run() {
while(true){
System.out.println(this.getName()+(tickets--));
if(tickets<1){
break;
}
}
}
}
通过实现Runnable接口,只产生了一个对象,但是可以创建任意多个线程,这些线程都是共享对象的属性的。
也就是说所有窗口提供的票数是固定的,不管从哪里买,票数都会发生变化
public class SharedDataThreadDemo {
public static void main(String[] args) {
/*
TicketRunnable runnable=new TicketRunnable();
Thread t1=new Thread(runnable,"窗口1");
t1.start();
Thread t2=new Thread(runnable,"窗口2");
t2.start();
}
}
class TicketRunnable implements Runnable{
private int tickets=5;
@Override
public void run() {
while(true){
System.out.println(Thread.currentThread().getName()+":"+(tickets--));
if(tickets<1){
break;
}
}
}
}