线程,公共资源,监视器等在并发操作时是要特别注意的,除了使用线程安全的类,还需要多了解线程的操作
线程中断interrupt操作
说明
在执行线程过程中,突然被stop 是非常危险的行为,很可能会导致数据操作的异步,
所以停线程,使用以下几个操作来控制.
public void interrupt()
public boolean isInterrupted()
public static boolean interrupted()
线程内部判断 isInterrupted(), 来具体执行停线程操作, 注意!! 外部把此线程中断后,如果线程正在sleep(), 线程会抛出InterruptedException 错误,在catch中要重新interrupt()中断线程,此线程中断状态才OK!!
示例代码:
public static void main(String args[]) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
public void run() {
while (true) {
System.out.println("hello!");
if (Thread.currentThread().isInterrupted()) {
//判断是否被中断
System.out.println("线程被中断!c");
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("sleep 被中断,但此时中断状态为:"+Thread.currentThread().isInterrupted()+",线程不会退出");
// Thread.currentThread().interrupt(); //如果要退出,使用这个方法
}
Thread.yield();
}
}
},"t1");
t1.start();
Thread.sleep(100);
t1.interrupt(); //主线程让t1 中断
}
输出如下:
hello!
java.lang.InterruptedException: sleep interrupted
sleep 被中断,但此时中断状态为:false,线程不会退出
at java.lang.Thread.sleep(Native Method)
hello!
at thread.OneThread$1.run(OneThread.java:21)
at java.lang.Thread.run(Thread.java:744)
hello!
hello!
说明:虽然被外部中断,但sleep被中断后,状态没改过来,所以线程没有bread,会继续执行.
资源等待与释放wait 和 notify
说明:
这两个方法Object 的方法,
当 在 一个 对象 实例 上 调用 wait() 方法 后, 当前 线程 就会 在这 个 对象 上 等待。一直到其它线程调用了 notify 方法,
如果出现多个 线程wait了,则其它线程notify 后,随机一个线程能被通知(notifyAll则全部都会被通知).
wait 和 nofity 都要获取 对象监视器 后才能处理,
JAVA每个对象(Object/class) 都关联一个监视器,为了使数据不被多个线程访问,java 提供了同步块 以及 同步方法两种实现,一旦一段代码被嵌入到一个synchronized关键字中,意味着放入了监视区域,JVM在后台会自动为这段代码实现锁的功能。
一些说明
- 你可以使用wait和notify函数来实现线程间通信。你可以用它们来实现多线程(>3)之间的通信。
- 永远在synchronized的函数或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。
- 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。
- 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。
- 基于前文提及的理由,更倾向用 notifyAll(),而不是 notify()。
代码示例:
/**
* denggm
*/
public class WaitAndNotify {
final static Object object = new Object();
public static class R1 implements Runnable {
@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();// 此方法要用synchronized 包含,不然运行时抛出 java.lang.IllegalMonitorStateException
//t1等待 对象释放
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()+": T1 end");
}
}
}
public static class R2 implements Runnable {
@Override
public void run() {
synchronized (object) {
System.out.println(System.currentTimeMillis()+": T2 start nofity one");
object.notify(); // 此方法要用synchronized 包含,不然运行时抛出 java.lang.IllegalMonitorStateException
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// notify 后,t1才会结束
System.out.println(System.currentTimeMillis()+": T2 end");
}
}
}
public static void main(String args[]){
String name = ManagementFactory.getRuntimeMXBean().getName();
String pid = name.split("@")[0];
System.out.println("进程号是:" + pid); //打印进程号,为了方便jvm工具获取打印栈
Runnable r1 = new R1();
Runnable r2 = new R2();
new Thread(r1,"t1").start();
new Thread(r2,"t2").start();
}
}
其结果为:
1519544750664: T1 start,
1519544750664: T1 wait for object
1519544750664: T2 start nofity one
1519544750664: T2 end
1519544752664: T1 end
查看java栈
为了方便查看 java的线程情况,面的例子把R2改成如下,然后用jstack <进程号> 来查看。
public static class R2 implements Runnable {
@Override
public void run() {
synchronized (object) {
System.out.println(System.currentTimeMillis()+": T2 start nofity one");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
object.notify(); // 此方法要用synchronized 包含,不然运行时抛出 java.lang.IllegalMonitorStateException
// notify 后,t1才会结束
System.out.println(System.currentTimeMillis()+": T2 end");
}
}
}
在 R2 执行 Thread.sleep(20000)时查看线程,截取关键内容如下所示:
2018-02-25 15:51:06
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.0-b70 mixed mode):
"DestroyJavaVM" #16 prio=5 os_prio=0 tid=0x00000000024b8000 nid=0x14e0 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"t2" #15 prio=5 os_prio=0 tid=0x000000000a73e800 nid=0x2718 waiting on condition [0x000000000d0bf000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at thread.WaitAndNotify$R2.run(WaitAndNotify.java:36)
- locked <0x00000000d6382078> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:744)
"t1" #14 prio=5 os_prio=0 tid=0x000000000a73d800 nid=0x27f8 in Object.wait() [0x000000000cd9f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d6382078> (a java.lang.Object)
at java.lang.Object.wait(Object.java:502)
at thread.WaitAndNotify$R1.run(WaitAndNotify.java:18)
- locked <0x00000000d6382078> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:744)
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000000254f800 nid=0x1dac in Object.wait() [0x000000000b59e000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d6488b20> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:142)
- locked <0x00000000d6488b20> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:158)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x00000000095d4800 nid=0x26e0 in Object.wait() [0x000000000b84f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d64a8178> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:157)
- locked <0x00000000d64a8178> (a java.lang.ref.Reference$Lock)
"Service Thread" #13 daemon prio=9 os_prio=0 tid=0x000000000a6cd800 nid=0x2554 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
........
如图,t1 和 t2线程非常明显,一个是TIMED_WAITING (sleeping),另一个是 WAITING (on object monitor)
suspend线程挂起和继续执行resume
这两个方法被废弃,因为不会释放资源.我们自己设计一个方法挂起线程
方法:
volatile boolean suspendme = false;
// 线程挂起
public void suspendMe(){
suspendme = true;
}
//释放
public void resumeMe(){
suspendme = false;
//释放本身的线程
synchronized (this) {
notifyAll();
}
}
//提供以上的这些方法来操作,在挂起要控制的地方:
while (suspendme) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
测试
我们设计一个方法来测试这个方法,执行逻辑为:
- 让t1和t2交替占用u (由于占用监视器不是固定的,所以只能说是大体上是交替执行的)
- t1挂起
- 查看是否只有t2占用 u
- t1释放
- 查看是否交替了
代码实现:
/**
* denggm
*
* 让t1和t2交替占用u (由于占用监视器不是固定的,所以只能说是大体上是交替执行的)
* t1挂起
* 查看是否只有t2占用 u
* t1释放
* 查看是否交替了
*
*/
public class SuspendOper {
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) {
notifyAll();
}
}
@Override
public void run() {
while (true) {
synchronized (this) {
while (suspendme) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (u) {
System.out.println("u in changeObjectThread");
}
try {
Thread.sleep(500);
//由于在不占用u的情况下睡了200ms,会导致,
// 在这个过程中u的监视权很有可能给了了其它线程,现象就是,
// 本线程占用了u显示了一下,其它线程很快又占用了u来显示,像是交替显示
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread.yield(); //谦让一下,
}
}
}
}
public static class ReadObjectThread extends Thread {
@Override
public void run() {
while (true) {
synchronized (u) {
System.out.println("u in readObjectThread");
}
try {
Thread.sleep(500);
//由于在不占用u的情况下睡了200ms,会导致,
// 在这个过程中u的监视权很有可能给了了其它线程,现象就是,
// 本线程占用了u显示了一下,其它线程很快又占用了u来显示,像是交替显示
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread.yield();//谦让一下
}
}
}
public static void main(String args[]) throws InterruptedException {
ChangeObjectThread t1 = new ChangeObjectThread();
ReadObjectThread t2 = new ReadObjectThread();
t1.start();
t2.start();
//现在应该是交替进程
Thread.sleep(3000);
//
//挂起 t1后,则只有t2在执行了
t1.suspendMe();
System.out.println("挂起t1线程-----------------------------------");
//
Thread.sleep(3000);
//
System.out.println("释放t1线程-----------------------------------");
t1.resumeMe();
}
}
执行的结果是:
刚开始大体交替,中间只在t2中,后面又大体交替.
u in changeObjectThread
u in readObjectThread
u in changeObjectThread
u in changeObjectThread
u in readObjectThread
u in readObjectThread
u in changeObjectThread
u in readObjectThread
u in changeObjectThread
挂起t1线程-----------------------------------
u in readObjectThread
u in readObjectThread
u in readObjectThread
u in readObjectThread
u in readObjectThread
u in readObjectThread
释放t1线程-----------------------------------
u in changeObjectThread
u in changeObjectThread
u in readObjectThread
u in changeObjectThread
u in readObjectThread
u in readObjectThread
u in changeObjectThread
u in readObjectThread
u in changeObjectThread
u in changeObjectThread
u in readObjectThread
join的使用
说明:
线程的方法,让当前线程等待 目标线程完成
示例代码:
/**
* denggm
*/
public class JoinThread {
static int i = 0;
public static void main(String args[]) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
public void run() {
for (int j = 0; j < 10; j++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
});
t1.start();
t1.join(); //主线程等待t1的完成
// 如果不加这个,下面的打印很可能会为0
System.out.println("i的值为:"+i);
}
}
参考资料:
<实战java高并发程序设计>
http://ifeve.com/monitors-java-synchronization-mechanism/