范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

5个步骤,教你瞬间明白线程和线程安全

  记得刚来杭州面试的时候,有一家公司的技术总监问了我这样一个问题:你来说说有哪些线程安全的类?我心里一想,这我早都背好了,稀里哗啦说了一大堆。
  他又接着问:那你再来说说什么是线程安全?——然后我就GG了。说真的,我们整天说线程安全,但是对于什么是线程安全我们真的了解吗?之前的我真的是了解甚微,那么我们今天就来聊聊这个问题。
  在探讨线程安全之前,我们先来聊聊什么是进程。 什么是进程?
  电脑中时会有很多单独运行的程序,每个程序有一个独立的进程,而进程之间是相互独立存在的。比如下图中的QQ、酷狗播放器、电脑管家等等。
  什么是线程?
  进程想要执行任务就需要依赖线程。换句话说,就是进程中的最小执行单位就是线程,并且一个进程中至少有一个线程。
  那什么是多线程?提到多线程这里要说两个概念,就是串行和并行,搞清楚这个,我们才能更好地理解多线程。
  所谓串行,其实是相对于单条线程来执行多个任务来说的,我们就拿下载文件来举个例子:当我们下载多个文件时,在串行中它是按照一定的顺序去进行下载的,也就是说,必须等下载完A之后才能开始下载B,它们在时间上是不可能发生重叠的。
  并行:下载多个文件,开启多条线程,多个文件同时进行下载,这里是严格意义上的,在同一时刻发生的,并行在时间上是重叠的。
  了解了这两个概念之后,我们再来说说什么是多线程。举个例子,我们打开腾讯管家,腾讯管家本身就是一个程序,也就是说它就是一个进程,它里面有很多的功能,我们可以看下图,能查杀病毒、清理垃圾、电脑加速等众多功能。
  按照单线程来说,无论你想要清理垃圾、还是要病毒查杀,那么你必须先做完其中的一件事,才能做下一件事,这里面是有一个执行顺序的。
  如果是多线程的话,我们其实在清理垃圾的时候,还可以进行查杀病毒、电脑加速等等其他的操作,这个是严格意义上的同一时刻发生的,没有执行上的先后顺序。
  以上就是,一个进程运行时产生了多个线程。
  在了解完这个问题后,我们又需要去了解一个使用多线程不得不考虑的问题——线程安全。
  今天我们不说如何保证一个线程的安全,我们聊聊什么是线程安全?因为我之前面试被问到了,说真的,我之前真的不是特别了解这个问题,我们好像只学了如何确保一个线程安全,却不知道所谓的安全到底是什么!什么是线程安全?
  既然是线程安全问题,那么毫无疑问,所有的隐患都是在多个线程访问的情况下产生的,也就是我们要确保在多条线程访问的时候,我们的程序还能按照我们预期的行为去执行,我们看一下下面的代码。Integer count = 0;     public void getCount() {         count ++;        System.out.println(count);    }
  很简单的一段代码,下面我们就来统计一下这个方法的访问次数,多个线程同时访问会不会出现什么问题,我开启的3条线程,每个线程循环10次,得到以下结果:
  我们可以看到,这里出现了两个26,出现这种情况显然表明这个方法根本就不是线程安全的,出现这种问题的原因有很多。
  最常见的一种,就是我们A线程在进入方法后,拿到了count的值,刚把这个值读取出来,还没有改变count的值的时候,结果线程B也进来的,那么导致线程A和线程B拿到的count值是一样的。
  那么由此我们可以了解到,这确实不是一个线程安全的类,因为他们都需要操作这个共享的变量。其实要对线程安全问题给出一个明确的定义,还是蛮复杂的,我们根据我们这个程序来总结下什么是线程安全。
  当多个线程访问某个方法时,不管你通过怎样的调用方式、或者说这些线程如何交替地执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的。
  搞清楚了什么是线程安全,接下来我们看看Java中确保线程安全最常用的两种方式。先来看段代码。public void threadMethod(int j) {      int i = 1;      j = j + i; }
  大家觉得这段代码是线程安全的吗?
  毫无疑问,它绝对是线程安全的,我们来分析一下,为什么它是线程安全的?
  我们可以看到这段代码是没有任何状态的,就是说我们这段代码,不包含任何的作用域,也没有去引用其他类中的域进行引用,它所执行的作用范围与执行结果只存在它这条线程的局部变量中,并且只能由正在执行的线程进行访问。当前线程的访问,不会对另一个访问同一个方法的线程造成任何的影响。
  两个线程同时访问这个方法,因为没有共享的数据,所以他们之间的行为,并不会影响其他线程的操作和结果,所以说无状态的对象,也是线程安全的。添加一个状态呢?
  如果我们给这段代码添加一个状态,添加一个count,来记录这个方法并命中的次数,每请求一次count+1,那么这个时候这个线程还是安全的吗?public class ThreadDemo {     int count = 0; // 记录方法的命中次数     public void threadMethod(int j) {         count++ ;         int i = 1;         j = j + i;    } }
  很明显已经不是了,单线程运行起来确实是没有任何问题的,但是当出现多条线程并发访问这个方法的时候,问题就出现了,我们先来分析下count+1这个操作。
  进入这个方法之后首先要读取count的值,然后修改count的值,最后才把这把值赋值给count,总共包含了三步过程:"读取"一>"修改"一>"赋值",既然这个过程是分步的,那么我们先来看下面这张图,看看你能不能看出问题:
  可以发现,count的值并不是正确的结果,当线程A读取到count的值,但是还没有进行修改的时候,线程B已经进来了,然后线程B读取到的还是count为1的值,正因为如此所以我们的count值已经出现了偏差,那么这样的程序放在我们的代码中,是存在很多的隐患的。如何确保线程安全?
  既然存在线程安全的问题,那么肯定得想办法解决这个问题,怎么解决?我们说说常见的几种方式。1、synchronized
  synchronized关键字,就是用来控制线程同步的,保证我们的线程在多线程环境下,不被多个线程同时执行,确保我们数据的完整性,使用方法一般是加在方法上。public class ThreadDemo {     int count = 0; // 记录方法的命中次数     public synchronized void threadMethod(int j) {         count++ ;         int i = 1;         j = j + i;    } }
  这样就可以确保我们的线程同步了,同时这里需要注意一个大家平时忽略的问题,首先synchronized锁的是括号里的对象,而不是代码,其次,对于非静态的synchronized方法,锁的是对象本身也就是this。
  当synchronized锁住一个对象之后,别的线程如果想要获取锁对象,那么就必须等这个线程执行完释放锁对象之后才可以,否则一直处于等待状态。
  注意点:虽然加synchronized关键字,可以让我们的线程变得安全,但是我们在用的时候,也要注意缩小synchronized的使用范围,如果随意使用时很影响程序的性能,别的对象想拿到锁,结果你没用锁还一直把锁占用,这样就有点浪费资源。2、Lock
  先来说说它跟synchronized有什么区别吧,Lock是在Java1.6被引入进来的,Lock的引入让锁有了可操作性,什么意思?就是我们在需要的时候去手动的获取锁和释放锁,甚至我们还可以中断获取以及超时获取的同步特性,但是从使用上说Lock明显没有synchronized使用起来方便快捷。我们先来看下一般是如何使用的:private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类     private void method(Thread thread){        lock.lock(); // 获取锁对象        try {            System.out.println("线程名:"+thread.getName() + "获得了锁");            // Thread.sleep(2000);        }catch(Exception e){            e.printStackTrace();        } finally {            System.out.println("线程名:"+thread.getName() + "释放了锁");            lock.unlock(); // 释放锁对象        }    }
  进入方法我们首先要获取到锁,然后去执行我们业务代码,这里跟synchronized不同的是,Lock获取的所对象需要我们亲自去进行释放,为了防止我们代码出现异常,所以我们的释放锁操作放在finally中,因为finally中的代码无论如何都是会执行的。
  写个主方法,开启两个线程测试一下我们的程序是否正常:public static void main(String[] args) {        LockTest lockTest = new LockTest();         // 线程1        Thread t1 = new Thread(new Runnable() {             @Override            public void run() {                // Thread.currentThread()  返回当前线程的引用                lockTest.method(Thread.currentThread());            }        }, "t1");         // 线程2        Thread t2 = new Thread(new Runnable() {             @Override            public void run() {                lockTest.method(Thread.currentThread());            }        }, "t2");         t1.start();        t2.start();    }
  结果:
  可以看出我们的执行,是没有任何问题的。
  其实在Lock还有几种获取锁的方式,我们这里再说一种,就是tryLock()这个方法跟Lock()是有区别的,Lock在获取锁的时候,如果拿不到锁,就一直处于等待状态,直到拿到锁,但是tryLock()却不是这样的,tryLock是有一个Boolean的返回值的,如果没有拿到锁,直接返回false,停止等待,它不会像Lock()那样去一直等待获取锁。
  我们来看下代码:private void method(Thread thread){        // lock.lock(); // 获取锁对象        if (lock.tryLock()) {            try {                System.out.println("线程名:"+thread.getName() + "获得了锁");                // Thread.sleep(2000);            }catch(Exception e){                e.printStackTrace();            } finally {                System.out.println("线程名:"+thread.getName() + "释放了锁");                lock.unlock(); // 释放锁对象            }        }    }
  结果:我们继续使用刚才的两个线程进行测试可以发现,在线程t1获取到锁之后,线程t2立马进来,然后发现锁已经被占用,那么这个时候它也不在继续等待。
  似乎这种方法,感觉不是很完美,如果我第一个线程,拿到锁的时间,比第二个线程进来的时间还要长,是不是也拿不到锁对象?
  那我能不能,用一中方式来控制一下,让后面等待的线程,可以等待5秒,如果5秒之后,还获取不到锁,那么就停止等,其实tryLock()是可以进行设置等待的相应时间的。private void method(Thread thread) throws InterruptedException {        // lock.lock(); // 获取锁对象         // 如果2秒内获取不到锁对象,那就不再等待        if (lock.tryLock(2,TimeUnit.SECONDS)) {            try {                System.out.println("线程名:"+thread.getName() + "获得了锁");                 // 这里睡眠3秒                Thread.sleep(3000);            }catch(Exception e){                e.printStackTrace();            } finally {                System.out.println("线程名:"+thread.getName() + "释放了锁");                lock.unlock(); // 释放锁对象            }        }    }
  结果:看上面的代码,我们可以发现,虽然我们获取锁对象的时候,可以等待2秒,但是我们线程t1在获取锁对象之后,执行任务缺花费了3秒,那么这个时候线程t2是不在等待的。
  我们再来改一下这个等待时间,改为5秒,再来看下结果:private void method(Thread thread) throws InterruptedException {        // lock.lock(); // 获取锁对象         // 如果5秒内获取不到锁对象,那就不再等待        if (lock.tryLock(5,TimeUnit.SECONDS)) {            try {                System.out.println("线程名:"+thread.getName() + "获得了锁");            }catch(Exception e){                e.printStackTrace();            } finally {                System.out.println("线程名:"+thread.getName() + "释放了锁");                lock.unlock(); // 释放锁对象            }        }    }
  结果:这个时候我们可以看到,线程t2等到5秒获取到了锁对象,执行了任务代码。
  以上就是使用Lock,来保证我们线程安全的方式。

足跟长了骨刺,走路时钻心疼,有什么方法可以快速止痛?您这种情况多数是由于足底筋膜炎导致的疼痛,一般都会在晨起的时候出现明显的疼痛,炎症比较重的时候走路量多或走的时间长的情况就会出现疼痛,确实很痛苦!一般治疗不建议求快,若想快速止痛可有什么可以长期做的兼职?这57个兼职是可以长期做的,一般月收入轻松上万元也很容易的。记得收藏(上等干货注意珍藏)众包类(1)阿里众包(2)腾讯搜活帮(3)百度众测(4)有道众包(5)京东微工(6)龙猫众包美国债务违约,对我们平常人的生活有多大影响?美国债务违约对我们平民百姓的生活不会有多大影响!那就好比个人在经济活动中遇到赖皮人,欠万八的钱不还,或者丢个钱包。我们的经济活动可照常运行,更影响不到我们的生活。而美国赖帐不还形成欧洲足坛刺激一夜世界第174爆冷,法国波兰10,荷兰3球完胜!头条创作挑战赛北京时间3月28日欧洲杯预选赛小组赛第2轮继续展开角逐,世界排名第3的法国对阵第48的爱尔兰世界排名第6的荷兰对战第200的直布罗陀等等,接下来我们就来看看各场比赛的MLB战报张育成敲出安打又完成双杀守备红袜却惜败勇士进入红袜队开季名单的台湾旅美棒球手张育成,在北京时间28日凌晨1点05分开打的红袜勇士一战担任先发第9棒守二垒,第2个打席敲出全队本战首安,还是二垒安打长打,可惜红袜队此役打击火力广东男篮球迷反对,陶汉林加盟辽篮,会影响联赛平衡广东男篮球迷反对,陶汉林加盟辽篮,会影响联赛平衡近日,有消息称辽宁男篮有意引进广东男篮的小鲨鱼陶汉林,以填补中锋位置的空缺。然而,这一消息引起了广东男篮球迷的反对,他们认为这样的转足球风暴再传喜讯,一石激起千层浪,中甲冠军或受牵连最近,中国足坛的反腐行动持续进行,许多重要部门的领导都已经接连落网。这一连串的事件让球迷们看到了中国足协的反腐决心,但也可能会对昆山俱乐部造成影响。据了解,昆山俱乐部在上个赛季中甲火箭心仪东契奇?掘金独行侠酝酿5换2交易,牵涉穆雷小波特?独行侠队遭遇四连败,欧文在被交易到达拉斯之后,独行侠队取得了7胜13负的糟糕战绩,不仅季后赛资格不保,甚至连附加赛资格都很难得到,对于一支上赛季还在西部总决赛舞台上拼杀的球队来说,茅台酒厂是如何防止员工上班偷喝茅台的?其实这个问题近期也是很多酒友比较关注的,我也不知道为什么会突然火了,然后小帅也是受粉丝所托,今天就来讲讲这个话题。茅台酒厂如何防止员工在工作时候偷喝酒其实这个现象,在最早之前是有过怎样才能让卤味浸入肉中?怎样能让卤味浸入到肉中?大家好,我是小董,我的回答是不少朋友平时都喜欢喝酒,自然少不了的就是美味的下酒菜,对于下酒菜而言,味道越香越爽口,才能够更好的下酒。而卤肉就是个很不错的选择手机照相,可以把照片在照相馆洗出来吗?应该不存在问题,先挑选成像好的照片,用手机转接器下载的U盘,拿照相馆去,U盘输入电脑上就可以冲洗(不如意的地方让师傅调整哈),数码时代比胶片时代方便多啦撒花呲牙祈祷观代智能手机的拍
牛肉炒萝卜丝的家常做法,滑嫩入味,香辣脆甜又可口,太下饭了大家周末好,我是大叔爱生活美食,每天分享几道好吃的家常,和你一样交流和学习,希望能喜欢,周末两天过得也是比较快的,转眼明天又要上班了,真心不想上班呀,为了生活,不能休息,还要继续,他曾在战场上与死神擦肩而过,今日却用自己的遗体献给国家!他是一位抗美援朝老战士,也是一个忠诚的爱国者。他叫做张序熙。在抗美援朝战场上,张序熙曾被敌机扫射过,他与死神擦肩而过,生命得以保留。这个经历,让他更加珍视生命,也让他更加深刻地认识NBA今日红黑榜(3。20)今天NBA共进行了8场比赛,那么今天有哪些球员打出超级高水平,又有哪些卧龙凤雏需要抓紧找个班上。下面请看今日NBA红黑榜。红榜1。奥斯汀里夫斯(湖人)村曼巴今天上场31分钟,狂砍3春天时令蔬菜,鲜嫩好吃营养丰富,推荐八道家常做法,老少皆宜大家好这里是香姐说美食,每天分享好吃的家常菜。随着春天的到来,许多应季的蔬菜陆续上市,春天的时令蔬菜又鲜又嫩,营养价值也非常丰富,富含钙维生素和膳食纤维等,日常的饮食生活中,绿叶蔬你没吃过的葱油饼做法,多加1样食材,怎么烙都千层酥皮松软你没吃过的葱油饼做法,多加1样食材,怎么烙都千层酥皮松软。以前总是做不好葱油饼,每次做都特别硬,真的好难吃,后来跟邻居大姐在楼下闲聊,大姐说自己烙饼一绝,尤其是家常葱油饼烙得最好吃家常早餐最美味,营养健康味道香,一周七天不重样,家人都爱吃早餐在哪儿吃?可能大部分人都还是在家里吃早餐吧!营养健康味道香,家里的早餐也可以做到一周七天不重样。每天在朋友圈里晒早餐,也经常会收到朋友的反馈,说按照我的早餐去做了,受到了家人的老家有种藏在地里的美味雨后随处可见,外地人一般下不去嘴地皮菌,在我的家乡,俗称为雷公屎,看上去暗黑粘稠,不认识的人会觉得很恶心。它在当下这种季节是最容易生长的,就是一种在雨后潮湿的地面上经常会出现的藻类植物。地皮菌从采摘到成菜这个过程春天爱吃的2道凉拌菜,鲜香脆嫩多汁,爽口开胃顺气,越吃越爱春天气温干燥,身体也会跟着有些燥热,吃一些凉拌菜,做法简单少油烟,还爽口开胃顺气。今儿跟大家分享的2道凉拌菜,鲜香脆嫩汁水多,其中的豆芽菜春笋,属于生发之物,尤其适合此季食用。凉拌腾讯控股(00700)将于本周三(22日)公布22年业绩大行更新评级及目标价(表)智通财经APP获悉,腾讯控股(00700)将于本周三(22日)公布去年业绩,受去年内地游戏收入及新冠疫情影响云业务拖累,综合21间券商预测,腾讯2022年非国际财务报告准则纯利预计强信心开新局锚定万亿级目标吉林旅游产业跑步提速20日,由吉林省人民政府主办的2023年吉林省春季旅游项目集中开工活动在吉林省各地同时举行。本次集中开工的项目总数达72个,总投资1083亿元。这次活动是吉林省首次在旅游领域的项目贡菜,脆嫩爽口,香辣解腻,装罐冷藏可以吃到夏天贡菜到底是不是莴笋做的?当然不是。但是今天我们要分享是用莴笋挑战剁椒贡菜的做法,味道比笋嫩,比肉香。简单步骤1削去莴笋外面的硬皮,对半切开再切成劈叉的长条,这样方便晾晒。放置大太阳