我们知道线程,尤其是多线程在JavaSE开发中被广泛使用。我们就从如何创建一个线程开始说起
创建一个线程的两种方法:
1.从Thread继承:
public class MyThread extends Thread{
public void run(){
//线程处理逻辑代码
//...
}
}
启动线程
MyThread thread = new MyThread();
thread.start();
2.实现Runnable接口:
public class MyThread2 implements Runnable{
public void run(){
//线程处理逻辑代码
//...
}
}
启动线程
Thread thread = new Thread(new MyThread2());
thread.start();
由于Java只支持单一继承,一旦类从Thread继承,则再也不能从其他类继承了,但接口没有此限制。因此在
实际应用中,如果自己的线程类一定要从其他类继承,此种情况下必须以实现Runnable接口的方式来创建线程。
当Thread类的start方法被调用后,则我们自己写的run方法就会被执行。run方法执行完后,线程就退出,结束
了自己的生命周期。
在通常情况下,我们都需要线程保持较长的生命周期而不结束,有任务处理时交给线程处理,没任务时线程处于休
眠状态,让出CPU时间片。保持线程不退出的唯一办法就是run方法不结束。因此,在run方法中,通常是个while循环
保证线程不退出。
public class WorkThread implements Runnable{
private boolean canRun = true; //线程运行控制变量
private int interval = 200; //线程睡眠间隔
public WorkThread(){}
//结束线程
public void stopThread(){
canRun = false;
}
//线程处理方法
public void run(){
while(canRun){
//...
if(hasTask()){//有任务处理
handleTask(); //处理任务
}else{ //无任务时,线程休眠
try{
Thread.sleep(interval);
} catch(Exception e){
}
}
}
//退出前要释放相应资源
//....
}
private boolean hasTask(){
//...
return false;
}
private void handleTask(){
//...
}
}
public class ThreadDemo{
public static void main(String[] args){
WorkThread worker = new WorkThread();
Thread thread = new Thread(worker);
thread.start();
//其它代码
//...
//线程结束
worker.stopThread();
//等待线程结束
try{
thread.join(3000);
}catch(Exception e){
e.printStackTrace();
}
}
}
多线程同步:
synchronized关键字可以实现同步。
1.synchronized方法
public synchronized void addValue(int newval);
synchronized方法控制对类成员变量的访问:每个类实例对应一把锁,每个synchronized方法都必须获得调用该
方法的类实例的锁才能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到该方法返回时才将锁释放,
此后被阻塞的线程方能获得该锁,重新进入可执行状态。
在Java中,除了类实例外,每个类也对应一把锁,这样我们也可以将类的静态成员函数声明为synchronized,以
控制其对类的静态成员变量的访问。
如果逻辑代码多,同步处理代码少,synchronized方法效率就会比较低,这时使用synchronized块比较好
2.synchronized块
synchronized(syncObject){ //syncObject这里可以用this替换
//允许访问控制的代码
}
代码必须获得对象syncObject(类的实例或者类)的锁方能执行。灵活性高
线程阻塞:
在同步不够用的情况下引入了线程阻塞机制。阻塞指的是暂停一个线程的执行以等待某个条件的发生
1.sleep()方法
以毫秒为单位的一段时间作为参数,使得线程在指定的时间内进入阻塞状态,指定时间一过,线程重新进入可
执行状态
2.yield()方法
yield()方法使得线程放弃当前分的CPU时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分
得CPU时间。调用yield()的效果等价于调度程序认为该线程已经执行了足够的时间从而转到另一个线程。
3.wait()和notify()方法
两个方法配套使用,wait()使得线程进入阻塞状态,它有两种形式:以毫秒为单位的时间作为参数;没有参数。前者
当对应的notify()被调用或者超过指定时间时线程进入可执行状态,后者则必须对应的notify()被调用。
wait/notify隶属于Object类,因为这一对方法阻塞时要释放占用的对象锁,而锁是任何对象都具有的。这一对方法必须
在synchronized方法或者块中调用。原因是,只有在同步方法或块中当前线程才占有锁,才会有锁可以释放。同样调用这一
对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。
注意点:
1.调用notify()方法解除阻塞线程时,解除的线程是随机被选取的,所以编程时一定要小心,避免这种不确定性而产生的问题
2.notifyAll()用来唤醒所有被wait()方法阻塞的线程。当然,只有获得锁的那个线程才能进入可执行状态。
守护线程
守护线程一般被用于在后台为其它线程提供服务。例如垃圾回收线程就是一个守护线程。可以通过isDaemon()判断一个线程是否是
守护线程,也可以调用方法setDaemon()将一个线程设为守护线程
生产消费模型
生产者是一个线程,消费者是一个线程,两个线程之间通过队列来传送产品。
队列类的实现:
package product;
import java.util.*;
public class Queue {
// 队列的最大长度
private int queueMaxLength = Integer.MAX_VALUE;
// 用LinkedList存放队列元素
private LinkedList<Object> list = new LinkedList<Object>();
public Queue() {
}
public Queue(int queueMaxLength) {
this.queueMaxLength = queueMaxLength;
}
public int getQueueMaxLength() {
return queueMaxLength;
}
public void setQueueMaxLength(int queueMaxLength) {
this.queueMaxLength = queueMaxLength;
}
// 获得队列中当前元素的数量
public int getQueueSize() {
return list.size();
}
// 把对象放入队列尾部,返回true成功,返回false表示
// 队列已满无法放入
public synchronized boolean put(Object obj) {
if (list.size() < queueMaxLength) {
list.add(obj);
notifyAll();// 通知其它线程
return true;
}
return false;
}
// 把对象放入队列头部,返回true成功,返回false表示
// 队列已满无法放入
public synchronized boolean putHead(Object obj) {
if (list.size() < queueMaxLength) {
list.addFirst(obj);
notifyAll();// 通知其它线程
return true;
}
return false;
}
// 获取队列的头部元素,并从队列中移除。如果队列为空
// 则会一直阻塞
public synchronized Object poll() {
waitWhenNoElement(-1);
return list.poll();
}
// 获取队列的头部元素,并从队列中移除。如果队列为空
// 则会一直阻塞.timeout超时时间,单位毫秒。如果为0表示
// 不阻塞等待,直接返回
public synchronized Object poll(int timeout) {
waitWhenNoElement(timeout);
return list.poll();
}
// 获取队列的头部元素,不从队列中移除。如果队列为空
// 则会一直阻塞
public synchronized Object peek() {
waitWhenNoElement(-1);
return list.peek();
}
// 获取队列的头部元素,不从队列中移除。如果队列为空
// 则会一直阻塞.timeout超时时间,单位毫秒。如果为0表示
// 不阻塞等待,直接返回
public synchronized Object peek(int timeout) {
waitWhenNoElement(timeout);
return list.peek();
}
// 当队列为空时等待
private void waitWhenNoElement(int timeout) {
if (list.size() < 1) { // 队列为空
try {
if (timeout < 0) { // 小于0表示无限等下去
wait();
} else if (timeout > 0) { // 大于0时,等待指定时间。timeout=0时不等待
wait(timeout);
}
} catch (Exception e) {
}
}
}
}
下面是一个线程基类
package product;
//线程基类
public class BaseThread extends Thread {
private boolean bRun = true;
// 线程是否可以继续运行
protected boolean canRun() {
return bRun;
}
// 停止线程
public void stopThread() {
bRun = false;
}
// 睡眠
public static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (Exception e) {
}
}
}
生产者线程
package product;
public class Producer extends BaseThread{
private Queue queue;
private int productNo = 0; //产品编号
public Producer(Queue queue){
this.queue = queue;
}
public void run(){
while(canRun()){
// 先判断队列是否已满
if(queue.getQueueSize() >= queue.getQueueMaxLength()){
System.out.println("Queue is full,pause...");
sleep(500); //队列满的时候,暂停生产
} else{
Integer product = newProduct(); //生产一个商品
System.out.println("Produce product,id = "+product);
queue.put(product); //放入队列
Thread.yield(); //让出时间片
}
}
}
//生产产品
private Integer newProduct() {
// 用Integer对象来模拟一个产品
Integer product = new Integer(++productNo);
sleep(100); //模拟生产所需要消耗的时间
return product;
}
}
消费者线程
package product;
public class Consumer extends BaseThread{
private Queue queue;
public Consumer(Queue queue){
this.queue = queue;
}
public void run(){
while(canRun()){
Object obj = queue.poll(500);
if(obj != null){
consumeProduct(obj);
Thread.yield();
}
}
}
//消费掉产品
private void consumeProduct(Object obj) {
Integer product = (Integer)obj;
System.out.println("Comsume product,id = "+product);
sleep(500); //模拟消费产品所需要的时间
}
}
测试类
package product;
public class SynchronizeDemo {
public static void main(String[] args) {
Queue queue = new Queue(20);
Producer producer = new Producer(queue);
Consumer consumer = new Consumer(queue);
producer.start();
consumer.start();
BaseThread.sleep(5000);
producer.stopThread();
consumer.stopThread();
try {
producer.join(200);
consumer.join(200);
} catch (Exception e) {
}
}
}