线程基础知识_Synchronized_ThreadAPI_自定义锁_获取线程运行时异常

Synchronized

synchronized包含monitor enter, monitor exit 2个JVM指令(遵循happens-before原则), 执行monitor exit之前必须存在monitor enter指令.
由于每个对象与一个monitor产生关联, 线程执行monitor enter时, 就会获取monitor的lock, monitor中存在计数器, 用于记录当前lock被获取的情况, 当线程已经获取了lock, 再次进行线程重入, lock往上自增.
与之相反, 当线程执行到monitor exit时, 对应的monitor计数器就会自减

  • this monitor: synchronized修饰的对象是this
public synchronized void test(){//...
}
//A实现Runnable接口
A a = new A();
new Thread(a).start();
new Thread(a).start();
  • class monitor: synchronized修饰的对象是class
public synchronized static void test() {//...
}
或者
public void test() {synchronized(A.class) {//...}
}
//A实现Runnable接口
A a1 = new A();
A a2 = new A();
//此时a1 与a2 共用一个class monitor
new Thread(a1).start();
new Thread(a2).start();

Thread API

sleep, yield, wait, notify/notifyAll, interrupt, interrupted, join

  • sleep
    Thread.sleep(), 线程休眠, 但休眠不会释放锁资源, 线程从running => block的状态切换, 可以使用thread.interrupt中断睡眠
  • yield
    Thread.yield(), 当前线程放弃CPU资源(CPU资源不紧张, 将忽略), 线程从running => runnable的状态切换, 因此不能使用interrupt中断处于runnable状态的线程
  • wait
    线程放弃锁资源, 并进入与锁关联的waitSet集合, 状态变化: Running => Blocked, 可以设定
    object.wait()等价于object.wait(0), 表示阻塞时间是永远
    注: wait必须在同步代码内. waitSet依赖于锁资源, 没有在同步代码内, 抛出IllegalMonitorStateException
    wait与sleep相似点:
1. 都能使线程阻塞
2. 都是可中断
3. wait属于Object, sleep属于Thread
4. wait需要在同步代码块中, sleep不需要
5. wait状态下, 将会是否锁资源, sleep不会释放锁资源
6. sleep执行一段自动退出阻塞状态, wait(long)一样.
  • notify/notifyAll
    notify随机唤醒waitSet集合中某一线程, notifyAll唤醒waitSet集合中所有的线程, 被唤醒的线程重新争抢锁资源, 争抢到之后, 接着刚才wait的地方往后执行
    注: notify必须在同步代码内. waitSet依赖于锁资源, 没有在同步代码内, 抛出IllegalMonitorStateException
  • interrupt
    打断当前线程阻塞状态, 可使用interrupt进行中断的方法如下:
object.wait(), object(long), Thread.sleep(long), thread.join(), Selector.wakeup()
public void interrupt() {if (this != Thread.currentThread())checkAccess();synchronized (blockerLock) {Interruptible b = blocker;if (b != null) {//设置中断标识, interrupt0属于native方法interrupt0();          b.interrupt(this);return;}}interrupt0();}

当线程使用休眠方法进入阻塞状态后, 使用thread.interrupt设置中断标志, 随后线程被中断, 抛出InterruptedException, 线程终止运行
isInterrupt: 用于判断线程是否被中断, 从下面源码看出, 判断线程是否被中断, 不会清除中断标志

   public boolean isInterrupted() {//native方法, clearInterrupted设置为falsereturn isInterrupted(false);}

思考: wait, sleep, yield被中断后会不会清除中断标志

/*** 测试* @author regotto*/
public class InterruptTest {private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {synchronized (lock) {try {System.out.println("1.isInterrupted: " + Thread.currentThread().isInterrupted());lock.wait();} catch (InterruptedException e) {System.out.println("2.isInterrupted: " + Thread.currentThread().isInterrupted());}}});thread.start();TimeUnit.SECONDS.sleep(1);thread.interrupt();TimeUnit.SECONDS.sleep(1);System.out.println("3.isInterrupted: " + Thread.currentThread().isInterrupted());}
}

上述结果都为false, 由于isInterrupt不会清除中断标志, 因此得出结论, 中断标志是被wait清除的, 同理sleep, yield也是一样的.

  • interrupted: 该方法直接看源码
public static boolean interrupted() {return currentThread().isInterrupted(true);}

从源码可以看出, 该方法就是调用isInterrupt方法, 但是, 注意传的参数是true, 意味着, 调用该方法后, 将会清除中断标志,

  • join
    A join B, B blocked 直到 A 变为terminal状态
    join源码如下:
    public final synchronized void join(long millis)throws InterruptedException {long base = System.currentTimeMillis();long now = 0;if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}if (millis == 0) {while (isAlive()) {wait(0);}} else {while (isAlive()) {long delay = millis - now;if (delay <= 0) {break;}wait(delay);now = System.currentTimeMillis() - base;}}}

根据源码得出如下结论:
join内部机制就是wait, 一定记住, 调用wait时, 谁调用, 谁进入blocked, 不要搞混了

/*** 测试join,join内部使用wait,使调用join的线程进行wait,当任务线程执行完,JVM底层自动调用notify* @author regotto*/
public class JoinTest {public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + ": " + i);}}, "t1");t1.start();try {//调用join线程是main线程,main线程进入join后处于wait状态,直到t1线程执行完,才notify//notify的过程由JVM底层自动调用//此时的join是相对于main,main线程是最终的调用者t1.join();} catch (InterruptedException e) {e.printStackTrace();}for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + ": " + i);}}
}

测试join状态下的interrupt

/*** 测试join状态下的interrupt* @author regotto*/
public class JoinSateExecuteInterrupt {public static void main(String[] args) {Thread main = Thread.currentThread();Thread thread = new Thread(() -> {while (true) {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + " interrupt");e.printStackTrace();break;}}});thread.start();//此处提前设置中断状态
//        main.interrupt();thread.interrupt();try {thread.join();} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + " interrupt");e.printStackTrace();}
//        thread.interrupt(); //此处调用interrupt不能使join方法进入interrupt,因为一直执行thread的run操作,main线程的此行代码永远不会执行}
}

根据Thread API实现自定义锁

需求分析: 实现synchronized的功能, 保证当线程处于阻塞状态可被中断(此处的阻塞代表线程在等待获取对象monitor), 线程在阻塞状态下可以计时, 避免线程资源浪费.
详细设计: 使用wait, notify, 标志变量设计lock, 模拟synchronized同步过程, A线程进入同步代码块, 将标志变量变为true, 此时wait有效, B进入线程进入阻塞状态, 当A线程执行完毕, 调用notifyAll, 唤醒被阻塞的线程, 在此过程中, 进入阻塞状态下的线程可被中断.

抽象接口定义:

package com.concurrent.definelock;import java.util.List;
import java.util.concurrent.TimeoutException;public interface Lock {void lock() throws InterruptedException;void lock(long mills) throws InterruptedException, TimeoutException;void unlock();/*** 用于获取处于阻塞状态的所有线程* @return list<thread>*/List<Thread> getBlockedThreads();
}

实现类:

package com.concurrent.definelock;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;public class BooleanLock implements Lock {/*** currentThread: 当前拥有锁的线程* locked: 锁是被被获得,默认false未获得,当调用lock能准确加锁* blockedList: 存储哪些线程进入阻塞状态*/private Thread currentThread;private boolean locked = false;private final List<Thread> blockedList = new ArrayList<>();/*** 第一个线程进入的时候, 标记为加锁的线程, 剩下的线程进入之后全都wait* 直到该线程unlock,剩下的线程才重新唤醒* @throws InterruptedException*/@Overridepublic void lock() throws InterruptedException {synchronized (this) {while (locked) {if (!blockedList.contains(currentThread)){blockedList.add(currentThread);}System.out.println(Thread.currentThread().getName() + ": 进入wait状态");//所有线程被唤醒, 只有拿到锁的那个线程才能接着下去执行, 剩余锁都只能处于阻塞状态//wait可中断方法, 当捕捉到Interrupt信号的时候, 抛出异常, 线程中断this.wait();System.out.println(Thread.currentThread().getName() + ": 被唤醒");}blockedList.remove(currentThread);System.out.println(Thread.currentThread().getName() + ": 删除在blockList中值, 设置locked为true");this.locked = true;this.currentThread = Thread.currentThread();}System.out.println(Thread.currentThread().getName() + ": 结束lock方法, 已出synchronize模块");}/*** 传入mills <=0 立刻加锁也可尝试抛异常, 否则经过mills再加锁* @param mills* @throws InterruptedException* @throws TimeoutException*/@Overridepublic void lock(long mills) throws InterruptedException, TimeoutException {synchronized (this) {if (0 >= mills) {this.lock();} else {long remainingMills = mills;long endMills = System.currentTimeMillis() + remainingMills;while (locked) {if (0 >= remainingMills){throw new TimeoutException();}if (!blockedList.contains(Thread.currentThread())) {blockedList.add(Thread.currentThread());}this.wait(remainingMills);remainingMills = endMills - System.currentTimeMillis();}blockedList.remove(Thread.currentThread());this.locked = true;this.currentThread = Thread.currentThread();}}}@Overridepublic void unlock() {System.out.println(Thread.currentThread().getName() + ": 进入unlock");synchronized (this) {//不加此步操作,任何线程都将可以执行notifyif (currentThread == Thread.currentThread()) {//修改locked, 表示未加锁, 唤醒剩余线程System.out.println(Thread.currentThread().getName() + ": 设置locked=false");this.locked = false;this.notifyAll();}}}@Overridepublic List<Thread> getBlockedThreads() {return blockedList;}
}

测试类:

package com.concurrent.definelock;import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;/*** 测试自定义BooleanLock* @author regotto*/
public class BooleanLockTest {private final Lock lock = new BooleanLock();public void syncMethod() {try {lock.lock();TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public static void main(String[] args) {BooleanLockTest blt = new BooleanLockTest();//定义10个线程, 进行start测试IntStream.range(0, 10).mapToObj(i -> new Thread(blt::syncMethod)).forEach(Thread::start);}
}

获取线程运行时异常

线程执行过程中, 可以为特定线程/全局线程设置运行时异常捕获, 通常使用Thread.setDefaultUncaughtExceptionHandler进行设置.

package com.concurrent.dealthreadexception;import java.util.concurrent.TimeUnit;public class CaptureThreadException {public static void main(String[] args) {//设置线程回调接口, 使得run方法出现异常, 回调Hook能捕获(该操作类似于监听者模式)//如果当前线程组没有设置回调接口, 那么就去父group中查找, 直到找到, 一直找不到//就执行System.errThread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {System.out.println("回调Hook捕获" + thread.getName() + "的run抛出的异常: " + throwable.getMessage());});final Thread thread = new Thread(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}//run方法不能抛出异常, 就算抛出也无法捕获, 只能使用特定的Hook获取异常System.out.println(1 / 0);}, "Test0");thread.start();}
}

Thread.setDefaultUncaughtExceptionHandler源码如下:

    public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {SecurityManager sm = System.getSecurityManager();if (sm != null) {sm.checkPermission(new RuntimePermission("setDefaultUncaughtExceptionHandler"));}//设置默认的异常捕获处理器, 0defaultUncaughtExceptionHandler = eh;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468567.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

32位和64位系统区别及int字节数

一&#xff09;64位系统和32位有什么区别&#xff1f; 1、64bit CPU拥有更大的寻址能力&#xff0c;最大支持到16GB内存&#xff0c;而32bit只支持4G内存 2、64位CPU一次可提取64位数据&#xff0c;比32位提高了一倍&#xff0c;理论上性能会提升1倍。但这是建立在64bit操作系…

在统计学中参数的含义是指_《统计学》名词解释及公式

章节主要内容学习要点1.1统计及其应用领域什么是统计学概念&#xff1a;统计学&#xff0c;描述统计&#xff0c;推断统计。统计的应用领域统计在工商管理中的应用。统计的其他应用领域。1.2数据的类型分类数据、顺序数据、数值型数据概念&#xff1a;分类数据&#xf…

2019深圳入户攻略

我上次说了&#xff0c;等我把户口办好了&#xff0c;我就把攻略写出来&#xff0c;给大家一个参考&#xff0c;今天刚好是我最后的一个步骤&#xff0c;去派出所把身份证给办了&#xff0c;好了&#xff0c;我以后就是深圳人了&#xff0c;来深圳快十年了&#xff0c;现在才真…

NIO之ByteBuffer_NIO之网络IO_与ChannelNetty初窥门径

NIO之ByteBuffer与Channel 传统IO:byte[] < inputStream < 文件 > outputStream > byte[] NIO:文件 > inputChannel <> buffer <> outputChannel > 文件文件 < inputChannel <> outputChannel > 文件文件复制, 并测试ByteBuffer常用…

ubuntu 12.04 samba 服务器搭建

为什么要搭建samba 服务器 我在 windows 下安装了个虚拟机,然后想两边同步下资料,原来虚拟机是可以共享文件的,可是不知道什么见鬼了,就是不行,没办法了,我只好拿出我的杀手锏,安装 samba 安装 samba 工具 安装samba:sudo apt-get install samba 安装smbclient:sud…

mp4 拍摄时间如何看_时间不多了,如何备考期末最有效?这些复习技巧,看了你就会了...

再过不到一个月就要过年了&#xff0c;虽然天气越来越冷&#xff0c;但是有阻挡不了年度大戏“期末考”的前进步伐&#xff0c;尤其是对于紧张复习之中的高考备考生而言&#xff0c;高三第一学期的期末考就可以算是对自己第一轮复习的一个检验&#xff0c;如果成绩不理想&#…

vue实战学习第二天

1.怎么运行别人的项目 步骤一&#xff1a;搭建脚手架&#xff1a;npm i -g vue-cli 步骤二&#xff1a;vue init webpack 不要一直默认回车&#xff0c;去除一些不必要的依赖&#xff0c;减少代码的编写难度 步骤三&#xff1a;下载依赖的文件 npm i &#xff08;可能有些人会…

Netty之自定义RPC

需求分析 使用netty实现方法远程调用, 在client调用本地接口中方法时, 使用反射进行远程调用, server执行完结果后, 将执行结果进行封装, 发送到client RPC调用模型: 1. 服务消费方(client)以本地调用方式调用服务 2. client stub 接收到调用后负责将方法、参数等封装成能够…

vue垂直布局_vue实现长图垂直居上 vue实现短图垂直居中

大致效果如下图&#xff0c;只考虑垂直方向。长图可以通过滚动条看&#xff0c;短图居中效果&#xff0c;布局合理html代码(vue作用域内)&#xff1a;css代码&#xff1a;.box{height: 100%;//如高度等于网页高度overflow: auto;display: flex;flex-direction: column;justify-…

samba 服务器搭建

为什么要搭建samba 服务器我在 windows 下安装了个虚拟机&#xff0c;然后想两边同步下资料&#xff0c;原来虚拟机是可以共享文件的&#xff0c;可是不知道什么见鬼了&#xff0c;就是不行&#xff0c;没办法了&#xff0c;我只好拿出我的杀手锏&#xff0c;安装 samba。这个在…

Codis的源码编译生成tar包

一、Go环境的安装 1、下载地址 https://golang.org/dl/2、解压 tar -zxvf go1.7.1.linux-amd64.tar.gz -C /usr/local 3、修改配置文件   vi /etc/profile     export GOROOT/usr/local/go     export GOPATH/usr/local/data/go     export PATH$PATH:$GOROOT/b…

一直想说的,技术职业化

最近后台有人一直跟我说&#xff0c;为什么不好好写一篇技术比较强的文章&#xff0c;说实话&#xff0c;最近时间比较紧张&#xff0c;早上 8 点出门&#xff0c;晚上12点左右到家。刚好今天整理了一个不错文章的列表&#xff0c;明天发出来&#xff0c;希望给学习的同学们有点…

MyBatis初级入门及常见问题

入门案例 创建maven工程 项目目录结构: 首先在maven的pom.xml导入Mybatis和MySQL的依赖坐标: <dependencies><!--Junit测试依赖--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</…

科沃斯机器人招股_科沃斯机器人首次公开发行A股股票的初步询价公告

随着科沃斯机器人公开发行A股股票&#xff0c;朋友圈都有谈论它的声音。那么小编就跟一跟风来给各位详细介绍一下目前科沃斯机器人首次公开发行A股股票的价格有关内容。希望下文能过给你们带来更多的收获哦。首次公开发行A股股票发行安排及初步询价公告保荐人(主承销商)&#x…

读书笔记:Information Architecture for the World Wide Web, 3rd Edition 北极熊 第一部分 1-3...

Introducing Information Architecture 信息架构简介 Chapter 1 Defining Information Architecture 信息架构的意义&#xff08;我们盖房子&#xff0c;之后&#xff0c;房子影响我们&#xff09; A DefinitionTablets, Scrolls, Books, and Libraries 石板、卷轴、书籍&#…

Mybatis执行流程分析_自定义简易Mybatis框架

自定义简易Mybatis框架 Mybatis执行流程分析 Mybatis代码编写流程: Mybatis配置文件加载过程: 需求分析及技术概述 根据上述的功能结构图, 得出如下需求: 1. 需要具有配置文件加载模块. 2. 支持使用构建者进行SessionFactory构建. 3. 支持使用工厂模式构建Session对象. 4.…

给大家推荐一个优质Linux内核技术公众号-Linux阅码场

作为一个Linux 技术公众号的作者&#xff0c;我觉得有义务推荐优秀的公众号&#xff0c;推广内容&#xff0c;希望对大家的学习有所帮助~Linux阅码场是一个专注Linux内核和系统编程与调试调优技术的公众号&#xff0c;它的文章云集了国内众多知名企业一线工程师的心得。无论你工…

图数据库_ONgDB图数据库与Spark的集成

快速探索图数据与图计算图计算是研究客观世界当中的任何事物和事物之间的关系&#xff0c;对其进行完整的刻划、计算和分析的一门技术。图计算依赖底于底层图数据模型&#xff0c;在图数据模型基础上计算分析Spark是一个非常流行且成熟稳定的计算引擎。下面文章从ONgDB与Spark的…

MyBatis动态SQL_多表查询_延迟加载_缓存

POJO包装传递对象 //POJO实体 public class QueryConditionObject {private User user;//user get,set方法 }//Dao 接口 public interface UserMapper{List<User> findByCondition(QueryConditionObject qco); }//UserMapper.xml中的select标签 <select id"find…

2019 高考填报志愿建议

2019 高考填报志愿建议1、城市很关键&#xff0c;在大城市上学和小地方上学会有很大的不同&#xff0c;现在很多毕业生毕业后会往北上广深跑&#xff0c;很多原因是因为这里的就业机会多&#xff0c;薪资比内地好太多了&#xff0c;如果你大学就能在这样的地方上学&#xff0c;…