一. 概念
进程是指在系统中正在运行的一个应用程序;
线程是系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元。
对于操作系统而言其调度单元是线程。一个进程至少包括一个线程,通常将该线程称为主线程。
一个进程从主线程的执行开始进而创建一个或多个附加线程,就是所谓基于多线程的多任务!!
线程的状态:
线程有五种状态:新生状态,可运行状态,运行状态,阻塞状态和运行结束状态。
当一个线程类在创建了对象之后,就意味这一个线程的产生,
在调用start()方法之前为“新生状态”,调用start()方法让线程进入“可运行状态Runnable”,此时的线程可以被线程管理器调度;
一旦线程调度(获取到时间片)即从可运行状态进入到“运行状态Running”;
在运行过程中遇到阻塞事件(如:sleep(),wait(),锁等待……)便使线程变成 了“阻塞状态Blocked”;
直到阻塞事件消除(如:notify(),锁释放……)线程有由阻塞状态变回“可运行状态”;
当线程执行结束则为“结束状态”。
join()方法:
以A,B两个线程为例:如果在线程A中调用B.join()方法,表示让线程A处于等待状态,直到线程B执行结束后才接着执行
sleep()方法:
sleep()方法时让当前线程处于等待状态,等待时间由参数决定
sleep()方法的调用形式:Thread.sleep(时间毫秒数);
wait()方法:
使当前线程进入阻塞状态,直到有notify()方法或者notifyAll()方法唤醒
notify()方法:
唤醒一个由于wait()方法处于等待的线程
notifyAll()方法:
唤醒所有由于wait()方法处于等待的线程
调用一个Object的wait与notify/notifyAll的时候,必须保证调用代码对该Object是同步的,
也就是说必须在作用等同于synchronized(obj){......}的内部才能够去调用obj的wait与notify/notifyAll三个方法,否则就会报错:
java.lang.IllegalMonitorStateException:current thread not owner
wait方法和sleep方法都可以让线程等待若干时间,除了wait方法可以唤醒之外,另外一个主要区别是wait方法会释放目标对象的锁,而sleep方法不会释放。
二.创建
创建一个线程有以下两种方式[可以查阅JDK API]:
① 线程类继承Thread,重写run()方法,在run()方法中完成此线程所要完成的工作,直接创建线程类的对象,然后调用start()方法启动线程,默认 调用run()方法。
② 线程类实现Runnable接口,实现run()方法,在run()方法中完成此线程所要完成的工作;创建线程的时候要注意:
首先创建实现Runnable接口的线程类的对象t,然后通过Thread tt = new Thread(t);来创建线程tt。也是通过调用start()方法来启动线程。
要想创建一个线程类,实质上是必须实现Runnable接口,因为Thread本身也是实现了Runnable接口的[可以点击进入Thread看其具体实现,是实现了Runnable接口的]。
但是如果要调用start()方法来启动一个线程,就必须构建Thread类的对象:
如果是用第①中方式创建的线程类,那么他所创建的对象就可以隐式转换成Thread对象,所以可以直接调用start()方法;
如果使用第②种方式创建的线程类,那么使用这个线程类创建对象之后,还必须通过这个对象创建一个Thread类的对象。
守护线程:
如何设置成守护线程:setDeamon(true);
守护线程一般用来为其他的线程提供服务,当其他的线程全部执行完毕以后,无论守护线程是否执行完成都会自动终止。
线程优先级:
优先级范围:1—10
设置线程t优先级的方法:t.setPriority(1--10)
获取线程t优先级的方法:t.getPriority()
线程优先级的默认值是:5
线程优先级为10的优先级最高,为1的最低
下面是一个生产者与消费者的示例,加深对多线程的理解与运用!!!
package com.thread.src;
public class Test {
public static void main(String[] args) {
//工具类
Utils u = new Utils();
//创建一个生产者线程
Productor p = new Productor(u);
//创建一个消费者线程
Customer c = new Customer(u);
//启动生产者和消费者线程
p.start();
c.start();
}
}
package com.thread.src;
/**
* 生产者
* @author winy_lm
*
*/
public class Productor extends Thread{
//工具类
private Utils u;
//构造函数
public Productor(Utils u){
this.u = u;
}
//线程执行方法
public void run(){
while(true){
//每循环一次向data中放一个数据
//调用Utils类中的procuce()方法来生产
u.produce();
}
}
}
package com.thread.src;
/**
* 消费者
* @author winy_lm
*
*/
public class Customer extends Thread{
//工具类
private Utils u;
//构造函数
public Customer(Utils u){
this.u = u;
}
//线程执行方法
public void run(){
while(true){
//每次从data中取一个数据
u.consume();
}
}
}
package com.thread.src;
/**
* 工具类
* @author winy_lm
*
*/
public class Utils {
//被操作的数组,最多只能放三个,超过三个就等待,为0个的时候也等待
private int[] data = new int[3];
// 定义一个count用来标识data数据的个数
private int count = 0;
// 定义一个num用来表示,生产者上一次生产的数据
private int num = 0;
/*
* 如果一个类中的方法同时被2个或者多个线程访问, 那么我们就要把这个/这些方法同步,避免破坏数据完整性
*/
/**
* 此方法是完成生产向data放数据的功能
*/
synchronized public void produce() {
try {
if (count < 3) {
data[count] = num + 1;
System.out.println("生产者:" + (num + 1));
num = num + 1;
count++;
// 生产者生产数据之后 data中有数据了,消费者可以消费了
// 所以要唤醒消费者
notifyAll();
} else {
//等待被唤醒
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 此方法用来供消费者线程调用,消费一个数据
*/
synchronized public void consume() {
try {
if (count > 0) {
int m = data[count - 1];
data[count - 1] = 0;
count--;
System.out.println("消费者:" + m);
// 因为我们已经消费了一个数据,data有空位,可以生产了
// 所以我们调用notifyAll()来唤醒生产者生产
notifyAll();
} else {
// else表明data中是空的,消费者不能消费,只有等待生产者生产
//等待被唤醒
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
关于多线程,这里有篇很全面很详细的文章可以参考下:
点击这里