1)新建线程
Java提供了线程类Thread来创建多线程的程序。其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread类或其子类的实例对象。每个Thread对象描述了一个单独的线程。要产生一个线程,有两种方法:
◆ 需要从Java.lang.Thread类派生一个新的线程类,重载它的run()方法;
◆ 实现Runnalbe接口,重载Runnalbe接口中的run()方法。
2)终止线程
Thread.stop()方法在结束线程时,会直接终止线程,并且会立即释放这个线程所持有的锁。而这些锁恰恰是用来维持对象一致性的。如果此时,写线程写入数据正写到一半,并强行终止,那么对象就会被破坏,同时,由于锁已经被释放,另外一个等待该锁的读线程就顺理成章的读到了这个不一致的对象。
这个过程可以用一下代码模拟
package cn.guet.parallel;
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() {
// TODO Auto-generated method stub
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);
try {
Thread.sleep(100);
} catch (Exception 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());
}
};
}
}
}
public static void main(String[] args) throws Exception {
new ReadObjectThread().start();
while (true) {
Thread t = new ChangeObjectThread();
t.start();
Thread.sleep(150);
t.stop();
}
}
}
如果需要停止一个线程,只是需要自行决定线程何时退出就可以。用上述例子说明,只需将ChangeObjectTread线程增加一个stopMe()方法即可。
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);
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
u.setName(String.valueOf(v));
}
Thread.yield();
}
}
}
3)线程中断
Thread.interrupt()方法是一个实例方法。它通知目标线程中断,也就是设置中断标志位。中断标志位表示当前线程已经被中断。Thread.isInterrupted()方法也是实例方法,它判断当前线程是否有被中断(通过检查中断标志位)。最后的静态方法Thread.interrupted()也是用来判断当前线程的中断状态,但同时会清除当前线程的中断标志位状态。
public static void main(String[] args) throws Exception {
new ReadObjectThread().start();
Thread t = new Thread() {
@Override
public void run() {
while (true) {
if(Thread.currentThread().isInterrupted()) {
System.out.println("Interruted!");
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("Interruted When Sleep");
Thread.currentThread().interrupt();
}
Thread.yield();
}
}
};
t.start();
Thread.sleep(2000);
t.interrupt();
}
4)等待和通知
当一个对象实例上调用wait()方法后,当前线程就会在这个对象上等待。比如,线程A中,调用了obj.wait()方法,那么线程A就会停止继续执行,而转为等待状态。线程A一直等到其他线程调用了obj.notify()方法为止。这时,obj对象就俨然成为多个线程之间的有效通信手段。
一个简单地使用wait()和notify()的案例:
public class SimpleWN {
final static Object object = new Object();
public static class T1 extends Thread {
@Override
public void run() {
synchronized (object) {
System.out.println(System.currentTimeMillis() + "T1 start!");
try {
System.out.println(System.currentTimeMillis() + "T1 wait for object");
object.wait();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + "T1 end!");
}
}
}
public static class T2 extends Thread {
@Override
public void run() {
synchronized (object) {
System.out.println(System.currentTimeMillis() + "T2 start! notify one thread");
object.notify();
System.out.println(System.currentTimeMillis() + "T2 end!");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO: handle exception
}
}
}
}
public static void main(String[] args) {
Thread t1 = new T1();
Thread t2 = new T2();
t1.start();
t2.start();
}
}
5)挂起和继续执行线程
线程挂起(suspend)和继续执行(resume)是一对相反的操作,被挂起的线程,必须要等到resume()操作后,才能继续执行。并不推荐使用suspend()去挂起线程,因为suspend()在导致线程暂停的同时,并不会去释放任何资源。此时,其他线程想要访问被它暂用的锁时,都会被牵连,导致无法正常继续运行。
为理解suspend()的问题,演示程序如下:
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.start();
Thread.sleep(100);
t2.start();
t1.resume();
t2.resume();
t1.join();
t2.join();
}
}
改进后的代码如下:
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.suspendMe();
System.out.println("suspend t1 2 second");
Thread.sleep(2000);
System.out.println("resume t1");
t1.resumeMe();
}
}
}
6)等待线程结束(join)和谦让(yield)
第一个join()方法表示无限等待,它会一直阻塞当前线程,直到目标线程执行完毕。第二个方法给出了一个最大等待时间,如果超过给定时间目标线程还在执行,当前线程也会因为“等不及了”,而继续往下执行。
这里提供一个简单的join实例:
public class JoinMain {
public volatile static int i = 0;
public static class AddThread extends Thread {
@Override
public void run() {
for (i = 0; i < 10000000; i++) {
}
}
}
public static void main(String[] args) throws InterruptedException {
AddThread at = new AddThread();
at.start();
at.join();
System.out.println(i);
}
}
主函数中,如果不使用join()等待AddThread,那么得到i很可能是0或者一个非常小的数字。因为AddThread还没开始执行,i的值就已经被输出了。但在使用join()方法后,表示主线程愿意等待AddThread执行完毕,跟着AddTread一起往前走,故在join()返回时,AddThread已经执行完成,故i总是10000000。
注:本篇博客内容摘自《Java高并发程序设计》