1.进程和线程
- 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统的基础。
- 面向进程的程序设计中,进程是程序的基本执行实体。
- 面向线程的程序设计中,进程是线程的容器。
- 进程是程序的实体,而程序是指令,数据,以及其组织形式的描述。
2.Java中的线程操作
2.1新建线程
2.1.2Thread线程类
- 关键字new创建一个线程对象,然后调用对象的start()方法:
Thread t1 = new Thread();
t1.start();
- 线程对象Thread有一个run()方法,start()方法会新建一个线程并让这个线程执行run()方法。
- 直接使用run()方法,会在当前线程中串行调用run()方法。
2.1.3Runnable接口
也可以用Runnable接口新建线程,它只有一个run()方法,而且默认的Thread.run()就是调用内部的Runnable接口,因此使用Runnable更合理。
代码语言:javascript复制public interface Runnable {
public abstract void run();
}
Thread类有一个构造方法:
代码语言:javascript复制public Thread(Runnable target)
默认的Thread.start()方法调用的时候,新的线程就会执行Runnable.run()方法:
代码语言:javascript复制public void run(){
if(target != null){
target.run();
}
}
以下代码实现Runnable接口,并将该实例传入Thread,避免重载Thread.run():
代码语言:javascript复制public class CreateThread implements Runnable{
public static void main(String[] args){
Thread t1 = new Thread(new CreateThread());
t1.start();
}
@Override
public void run(){
System.out.println("Here is a Runnable!");
}
}
2.2 终止线程
一般来说,线程在执行完成之后就会结束。但是也可以手动关闭线程。
2.2.1Thread.stop()
- Thread.stop()方法可以结束线程,但是是直接终止线程,并立即释放这个线程所持有的锁。
- 该方法会导致数据不一致的问题,因此已经被标注为废弃,不要使用。
例如,以下代码没有错误信息,但是结果不一致了:
代码语言:javascript复制public class StopThreadUnsafe {
public static User u = new User();
public static class User {
int id;
String name;
public User() {
id = 0;
name = "0";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User [id = " id ", name = " name "]";
}
}
public static class ChangeObjectThread extends Thread {
@Override
public void run() {
while (true) {
synchronized (u) {
int v = (int) (System.currentTimeMillis() / 1000);
u.setId(v);
// do something else
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
u.setName(String.valueOf(v));
}
Thread.yield();
}
}
}
public static class ReadObjectThread extends Thread {
@Override
public void run() {
while (true) {
synchronized (u) {
if (u.getId() != Integer.parseInt(u.getName())) {
System.out.println(u.toString());
}
}
Thread.yield();
}
}
}
public static void main(String[] args) throws InterruptedException {
new ReadObjectThread().start();
while (true) {
ChangeObjectThread t = new ChangeObjectThread();
t.start();
Thread.sleep(150);
t.stop();
}
}
}
如果要安全的停止一个线程,可以自己决定停止线程的时机,例如将前例中的CreateObjectThread线程中增加一个stopMe()方法:
代码语言:javascript复制public static class ChangeObjectThread extends Thread {
volatile boolean stopme = false;
public void stopMe() {
stopme = true;
}
@Override
public void run() {
while (true) {
if(stopme) {
System.out.println("exit by stop me");
break;
}
synchronized (u) {
int v = (int) (System.currentTimeMillis() / 1000);
u.setId(v);
// do something else
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
u.setName(String.valueOf(v));
}
Thread.yield();
}
}
}
2.3 中断线程
- 中断线程并不会使线程立即退出,而是给线程发送通知,线程接到通知后的操作由线程执行决定。
- JVM中于线程中断有关的方法如下:
- Thread.interrupt(),是一个实例方法,通知线程中断,即设置中断标志位。中断标志位表示当前线程已经被中断了。
- Thread.isInterrupted(),也是一个实例方法,判断当前线程是否有中断(通过判断中断标志位)
- Thread.interrupted(),是一个静态方法,用来判断当前线程的中断状态,同时清除当前线程的中断状态。
- 方法签名为:
public void Thread.interrupt() //中断线程
public boolean Thread.isInterrupted() //判断线程是否中断
public static boolean Thread.interrupted() //判断是否中断,并清除当前中断状态
注意:中断后需要增加中断处理代码,不然中断不会发生作用。
Thread.sleep()函数
在循环体中,出现了sleep()或者wait()等操作,需要通过中断来识别。wait()在下一小节介绍,这里介绍sleep()方法。 Thread.sleep()函数的作用是让当前线程休眠若干时间,其函数签名为:
代码语言:javascript复制public static native void sleep(long millis) throws InterruptedException
它会抛出一个InterruptedException异常,不是运行时异常,程序必须捕获并处理他。当线程在sleep()休眠中被中断,这个异常就会产生。 注意:Thread.sleep()方法因为中断抛出异常时,会清除中断标记,如果不加处理,在下一次循环开始时,就无法捕捉这个中断,所以在异常处理中需要再次设置中断标记位。
2.4 等待(wait)和通知(notify)
- 等待(wait)方法和通知(notify)方法是为了支持多线程的协作而存在的。
- 这两个方法是在Object类中,即任何对象都能调用这两个方法。
- Object.wait()方法不能随便调用,需要包含在对应的synchronzied语句中。
- wait()和notify()方法都需要首先获得目标对象的一个监视器。
- Object.wait()和Thread.sleep()方法都能让线程等待若干时间,区别为:
- wait()方法可以被唤醒,sleep()方法需要等待时间结束
- wait()方法会释放目标对象的锁,而sleep()方法不会释放任何资源
- 其方法签名为:
public final void wait() throws InterruptedException
public final native void notify()
线程调用object.wait()方法,它会进入到object的等待队列。当object.notify()方法被调用时,对象会在线程队列中,随机选择一个线程,将其唤醒。 注意:这个选择是非公平的,完全随机。 使用例子:
代码语言:javascript复制package temp;
public class SimpleWN {
final static Object object = new Object();
public static class T1 extends Thread{
public void run() {
//获得object对象锁
synchronized(object) {
System.out.println(System.currentTimeMillis() ": T1 Start!");
try {
System.out.println(System.currentTimeMillis() ": T1 is wait for obejct!");
// 释放object的对象锁
object.wait();
}
catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() ": T1 is end");
}
}
}
public static class T2 extends Thread {
public void run() {
synchronized(object) {
System.out.println(System.currentTimeMillis() ": T2 Start!");
System.out.println("notify one thread!");
// 释放object对象锁
object.notify();
System.out.println(System.currentTimeMillis() ": T2 end!");
try {
// 休眠结束之后,才释放对象锁,T1才能继续执行
Thread.sleep(2000);
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Thread t1 = new T1();
Thread t2 = new T2();
t1.start();
t2.start();
}
}
2.5 挂起(suspend)和继续执行(resume)线程
- 被挂起(suspend)的线程,需要等到resume()操作后才能继续执行
- 这两个方法已经标注为废弃,不建议使用
- 废弃的原因是suspend()方法在导致线程暂停的同时,并不会释放任何锁资源,其他任何要范围被它暂时使用的锁,都无法正常运行。而被挂起的线程状态仍然是Runnable,影响对系统状态的判断。
以下例子会导致系统锁死:
代码语言:javascript复制public class BadSuspend {
public static Object u = new Object();
static ChangeObjectThread t1 = new ChangeObjectThread("t1");
static ChangeObjectThread t2 = new ChangeObjectThread("t2");
public static class ChangeObjectThread extends Thread{
public ChangeObjectThread(String name) {
super.setName(name);
}
@Override
public void run() {
synchronized(u) {
System.out.println("in " getName());
Thread.currentThread().suspend();
}
}
}
public static void main(String[] args) throws InterruptedException{
// t1
t1.start();
Thread.sleep(100);
t2.start();
t1.resume();
// 此时t2的状态依然是RUNNABLE
t2.resume();
t1.join();
t2.join();
}
}
为了达到相同的目的,可以用如下方法(使用wait()和notify()):
代码语言:javascript复制public class GoodSuspend {
public static Object u = new Object();
public static class ChangeObjectThread extends Thread{
// 标记变量,表明线程是否被挂起
volatile boolean suspendme = false;
public void suspendMe() {
suspendme = true;
}
public void resumeMe() {
suspendme = false;
synchronized (this){
notify();
}
}
@Override
public void run() {
while(true) {
synchronized(this) {
// 检查线程是否被挂起
while(suspendme) {
try{
wait();
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized(u) {
System.out.println("in ChangeObjectThread");
}
Thread.yield();
}
}
}
public static class ReadObjectThread extends Thread{
@Override
public void run() {
while(true) {
synchronized(u) {
System.out.println("in ReadObjectThread");
}
Thread.yield();
}
}
}
public static void main(String[] args) throws InterruptedException{
// 实例化线程对象
ChangeObjectThread t1 = new ChangeObjectThread();
ReadObjectThread t2 = new ReadObjectThread();
// 运行线程对象
t1.start();
t2.start();
Thread.sleep(1000);
// 挂起t1线程
t1.suspendMe();
System.out.println("suspend t1 2 sec");
Thread.sleep(2000);
System.out.println("resume t1");
t1.resumeMe();
}
}
2.6 等待线程结束(join)和谦让(yield)
- join签名如下,有两种:
// 阻塞当前线程,直到目标线程执行完毕
public final void join() throws InterruptedException
// 最多等待millies毫秒,之后继续执行
public final synchronised void join(long millis) throws InterruptedException
- join 的本质是调用线程wait()方法在当前线程对象实例上,它使得调用线程在当前线程对象上等待,被等待的线程会在调用结束前调用notifyAll()通知所有等待线程继续执行。JDK中join()实现的核心代码为:
while(isAlive()){
wati(0);
}
基础join()例子:
代码语言:javascript复制package temp;
public class JoinMain {
public volatile static int i = 0;
public static class AddThread extends Thread {
@Override
public void run() {
for (; i < 1000000; i )
;
}
}
public static void main(String[] args) throws InterruptedException {
AddThread at = new AddThread();
System.out.println(i);
at.start();
System.out.println(i);
at.join();
// join()保证最后输出的是1000000
System.out.println(i);
}
}
- yield()方法会使当前线程让出CPU,重新进行资源分配。