java多线程操作

1 篇文章 0 订阅
1 篇文章 0 订阅

线程,公共资源,监视器等在并发操作时是要特别注意的,除了使用线程安全的类,还需要多了解线程的操作

这里写图片描述

线程中断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在后台会自动为这段代码实现锁的功能。

一些说明
  1. 你可以使用wait和notify函数来实现线程间通信。你可以用它们来实现多线程(>3)之间的通信。
  2. 永远在synchronized的函数或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。
  3. 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。
  4. 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。
  5. 基于前文提及的理由,更倾向用 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/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值