Java多线程的基础知识总结——进程的建立
不得不说软件构造的实验虽然很难,但是都很有针对性,确实让我学到了不少。借着做实验的集合我来总结一下多线程的基础知识
要对多线程中各个线程的状态进行一个总结,就不得不看这张图
这张图包含了Thread的各种状态和进入状态使用的函数。
Java线程的一些重要状态
从上图可以看到线程一共有5种状态,但是我觉得需要理解的状态有3种
就绪状态(Runnable):当调用线程对象的start()方法,线程即进入就绪状态。这个状态只表示它可以开始了,并不表示它马上就会开始,在多个线程都进入就绪状态的情况下,cpu先执行哪个完全是随机的
运行状态(Running):一旦cpu选择某个就绪状态的线程并开始执行,这个线程就进入了执行状态,它就会沿着run()方法中的代码一行一行执行
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。而在此期间其他的线程不受影响继续执行。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态,直到被其他线程唤醒才会进入就绪状态(注意保证wait和notify的执行顺序,否则有可能发生永远不会被唤醒的情况);
2.同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态,直到锁被释放时才会进入就绪状态(并不是锁释放就会立刻开始运行,还是会和同样运行到这里其他线程争夺资源的使用权);
3.其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态结束、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
怎么创建一个进程
这一块网上内容很多有很详细,我就不仔细讲了,简略说说:
在Java中进程是一个类——Thread类。
第一种方法:我们可以直接继承Thread类,重写里面的run函数
public class Threadtest extends Thread{
@Override
public void run() {
//在这里重写你的方法
}
}
第二种方法:我们注意到Thread类的声明中,run()方法是如下声明的
/* What will be run. */
private Runnable target;
public void run() {
if (target != null) {
target.run();
}
}
说明第一种方法其实不是Thread希望我们使用的方法。target是一个继承了runnable接口的类的实例。如此,我们就可以声明一个类继承runnable接口,并实现run()方法,在Thread实例化时作为参数传入,这样Thread就可以执行我们写的类的run()方法了
public class Threadtest {
void test() {
Thread a=new Thread(new runit());
}
class runit implements Runnable {
public void run() {
//在这里写你的方法
}
}
}
第三种方法:虽然实现run()方法就可以实现一个进程对象,但run()方法并没有返回值,这对某些情况并不友好。使用封装的思想,Java为我们提供了一个新的接口callable<>,首先来看看它的使用方式
public class Threadtest {
void test() {
FutureTask<Integer> b=new FutureTask<Integer>(new runit())
Thread a=new Thread();
b.get(); //得到返回值
}
class runit implements Callable<Integer> {
public Integer call() {
//在这里写你的方法
return null;
}
}
}
可以看到实现callable接口的类要被FutureTask类封装之后才能传入Thread被执行。让我们仔细看看FutureTask是如何实现run()方法的
public void run() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
FutureTask向下调用了callable的call方法,得到了返回值,向上继承runnable方法。除此之外,它还提供了多种方法来实现检查进程的运行情况等等功能。所以我更推荐使用这种方式创建进程。