小工具      在线工具  汉语词典  css  js  c++  java

并发操作时需要特别关注线程、公共资源、监视器等。除了使用线程安全类之外,还需要更多地了解线程操作。

这里写图片描述

线程中断中断操作

阐明

在执行线程过程中,突然被stop 是非常危险的行为,很可能会导致数据操作的异步,
所以停线程,使用以下几个操作来控制.

public void interrupt()
public boolean isInterrupted()
public static boolean interrupted()

线程内部判断isInterrupted()专门执行线程停止操作。注意力!!线程被外部中断后,如果线程处于sleeping()状态,线程会抛出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的状态并没有改变,所以线程没有面包了,会继续执行。

资源等待与释放等待与通知

阐明:

这两个方法Object 的方法,
当 在 一个 对象 实例 上 调用 wait() 方法 后, 当前 线程 就会 在这 个 对象 上 等待。一直到其它线程调用了 notify 方法,
如果出现多个 线程wait了,则其它线程notify 后,随机一个线程能被通知(notifyAll则全部都会被通知).
wait 和 nofity 都要获取 对象监视器 后才能处理,

每个JAVA对象(Object/class)都与一个监视器相关联。为了防止数据被多个线程访问,Java提供了两种实现:同步块和同步方法。一旦一段代码中嵌入了synchronized关键字,就意味着将其放入监控区域后,JVM会自动在后台对这段代码实现锁定功能。

一些笔记
  1. 可以使用wait和notify函数来实现线程间通信。您可以使用它们来实现多个线程(>3)之间的通信。
  2. 在同步函数或对象中始终使用wait、notify和notifyAll,否则Java虚拟机将生成IllegalMonitorStateException。
  3. 始终在 while 循环中使用 wait 而不是 if 语句。这样,循环会在线程休眠之前和之后检查等待条件,如果条件实际上没有改变,则处理唤醒通知。
  4. 始终对多个线程之间共享的对象(生产者-消费者模型中的缓冲区队列)使用等待。
  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(正在睡眠),另一个是WAITING(在对象监视器上)

挂起线程挂起并恢复执行

这两种方法因资源不会被释放而被放弃。我们设计了一个方法来挂起线程。

方法:
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

使用连接

阐明:

Thread方法,让当前线程等待目标线程完成

示例代码:
/** * 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/

. . .

相关推荐

额外说明

C++中的栈和队列

原文链接:http://blog.csdn.net/zhy_cheng/article/details/8090346 使用标准库的栈和队列时,先包含相关的头文件 #include<stack> #include<queue> 定义栈如下: stack<

额外说明

ChatGPT会对我们日常生活带来什么影响?这些技术会改变我们学习阅读工作方式吗?

ChatGPT会对我们日常生活带来什么影响?这些技术会改变我们学习阅读工作方式吗? AI 这个话题很火,我也一直在关注着,很多人甚至觉得 AI 会改变世界,也许你会好奇:ChatGPT 会在三年内终结编程吗?AI有可能改变人的学习方式吗?AI 能否取代打

额外说明

BOM ------ client 系列

client 系列 client 事件属性返回当事件被触发时鼠标指针相对于浏览器页面(或客户区)的坐标。 客户区指的是当前窗口。 clientWidth <!DOCTYPE html> <html lang="en"> <head> <meta

额外说明

mysql explain执行计划详解

前言 在日常开发中,经常会碰到mysql性能调优的问题,比如说某个功能一开始使用的时候,响应挺快,但是随着时间的推移,应用的访问量,数据量上去之后,发现越来越慢,甚至更糟糕,通常来说,排除了网络相关的因素之后,大多数情况下都是由sql问题引起的 因此,如

额外说明

Python生成exe可执行文件的两种方法(py2exe和pyinstaller)

C:\Users\Administrator>python -m pip install py2exe D:\Python\Python38\python.exe: No module named pip 上述出现没有pip模块的错误,说明没有安装pip

额外说明

VB讲课笔记01:VB6.0安装与启动

VB讲课笔记01:VB6.0安装与启动 一、安装VB6.0 VB6.0安装在Windows7、8、10上必须设置安装程序的兼容性。 1、设置安装程序的兼容性

额外说明

[HDCTF 2023]YamiYami

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 涉及知识点 解题详细过程 session伪造 反弹shell 前言 从暑假末尾一直搁置,当时卡在反弹shell搞得离flag就差一步。不过最近一两天学习完反弹shell的知

额外说明

Python视频制作引擎Manim安装教程2021版(科学概念可视化)

Python视频制作引擎Manim安装教程2021版 0 写在前面 1 效果展示 2 安装教程(Windows) 2.1 安装ffmpeg 2.2 安装Latex 2.3 安装dvisvgm 2.4 安装Manim 3 测试与开发 0 写在前面 相信很多

额外说明

电脑系统缺少richtx32.ocx是如何解决的?

其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或者损坏了,这时你只需下载这个richtx32.ocx文件进行安装(前提是找到适合的版本),

额外说明

出现api-ms-win-core-localization-l1-2-0.dll找不到的解决方式

其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或者损坏了,这时你只需下载这个api-ms-win-core-localization-l1

ads via 小工具