package cn.xuetao.test;
public class Test1 extends Thread {
@Override
public void run() {
System.out.println("这里执行的是test1的run方法");
}
public static void main(String[] args) {
Test1 t1 = new Test1();
t1.start();
}
}
在java并行程序基础篇我们知道新建一个并行线程应当是new 线程对象之后调用start()方法,在这里为什么是调用start方法而不是直接调用run方法呢?首先调用run方法只会作用在当前线程中串行的执行run方法的代码,这只是一次方法的调用,并没有新建一个线程,而且直接调用run方法时并没有做你想做的事,如果你希望程序做你想做的事,那么应当重载run方法,将你希望做的事情加进去。如上面代码所示,我们创建一个线程t1,我们希望打印一句话,那么重载线程的run方法,调用start方法,运行结果如下:
那么如果我重载了run方法再调用run方法是什么结果呢,如图所示,
结果也是打印了这句话,那是不是意味着重载run方法后调用run方法就可以替代start方法呢!答案是否定的,在
基础篇可知start是新建了一个线程,而直接调用run方法则没有新建一个线程。
所以以后新建线程的时候需要调用的应该是start方法,而不是run方法。还有就是对于java来说是单继承的,继承的资源是宝贵
的。所以我们可以通过实现Runnable接口,它也有一个run方法。并且Thread类也是通过实现Runnble接口来实现的,如图所示:
此外在Thread类中还有个非常重要的构造方法叫 public Thread(Runnble target) 此方法传入一个接口的实例,在start()方法调用的时候,新的线程就会执行Runable.run()方法,实际上,默认的Thread.run()也是这么做的。所以上面的Test1也可以这么写
public class Test1 /*extends Thread*/ implements Runnable {
@Override
public void run() {
System.out.println("这里执行的是runnable中的run方法");
}
public static void main(String[] args) {
Thread t1 = new Thread(new Test1());
t1.start();
}
这种方式也是最为常见的方式。
2.2.2 终止线程
一般来说,线程在执行完毕之后就会结束,无需手动关闭,但是也有例外,比如说服务端的一个常驻线程本身就是一个无穷循环来提供某种服务,这种线程是不会正常终结的,那么我们需要终结一个线程的话在jdk的api中不难找到一个stop()的方法,该方法是一个废弃的方法吗,也就是说jdk随时有可能移除的方法,那么为什么该方法是个废弃的方法呢?原因在于stop()方法是一个比较暴力的方法,强行将执行一半的线程终结掉,这样会导致数据不一致的情况。因为它会立即释放掉这个线程所持有的锁,而这些锁恰恰是维持对象一致性的。书上举出一个示例代码如下:
package cn.xuetao.test;
public class StopThreadUnsafe {
public static User u = new User();
public static class User {
private int id;
private 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);
u.setName(String.valueOf(v));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// System.out.println("ChangeObjectThread的u:"+u);
}
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) {
Thread t = new ChangeObjectThread();
t.start();
Thread.sleep(150);
System.out.println("霖霖提醒您,下一步执行stop方法,此时的u属性为:"+u);
t.stop();
}
}
}
运行的结果为
没有运行出书上所说的id与name不一致的情况,我猜想是因为偶然性比较大,查看代码没有问题。
2.2.3 在java中线程中断是一种重要的线程协作的机制,上面我们说了stop()方法,指出该方法是一个废弃的方法,但是不要担心
jdk中是有更加强大的支持
严格上来说,线程中断并不会使线程立即退出,而是给线程发送一个通知,告诉目标线程有人希望你退出,接到通知后目标线程该如何处理那就是它自己的事情,而不是像stop方法的立即退出。
jdk提供了如下几个方法
public void Thread.interrupt() // 中断线程
public boolean Thread.isInterrupt() // 判断是否被中断
public static boolean Thread.interrupted() // 判断是否被中断,并清除当前中断状态
public void Thread.interrupt()方法是一个实例方法,它会通知目标线程中断,也就是设置中断标志位,中断标志位标识该线程
已经被中断了,public boolean Thread.isInterrupt() 判断是否被中断(通过检查中断标志位),public static boolean Thread.interrupted() 判断是否被中断,并清除当前中断状态。
package cn.xuetao.test;
public class Test2 extends Thread {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
Thread.yield(); // 当一个线程执行到yield方法的时候就会放弃执行权,其他线程就可以拿到执行权了
}
}
};
t1.start();
Thread.sleep(2000);
t1.interrupt();
System.out.println("霖霖提醒您t1线程是否被中断"+t1.isInterrupted());
}
}
在这里,虽然对t1进行了中断,但是在t1中并没有中断处理的逻辑,因此,即使t1线程被设置上了中断状态,但是这个状态也不会发生任何的作用,如果想t1在中断后退出,则必须为他增加相应的中断处理的代码。如:
package cn.xuetao.test;
public class Test2 extends Thread {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
if(Thread.currentThread().isInterrupted())
System.out.println("霖霖提醒您,这个线程需要中断,下面进行中断");
break;
}
Thread.yield(); // 当一个线程执行到yield方法的时候就会放弃执行权,其他线程就可以拿到执行权了
}
};
t1.start();
//Thread.sleep(2000);
t1.interrupt();
System.out.println("霖霖提醒您t1线程是否被中断"+t1.isInterrupted());
}
}
运行结果是