Java中的线程--线程的互斥与同步通信
Java中的线程之前也提到过,但是还是想再详细的学习一下,跟着张孝祥老师,系统的再学习一下。
一、线程中的互斥
线程安全中的问题解释:线程安全问题可以用银行中的转账
例题描述:
线程A与线程B分别访问同一个对象的方法,这样就会存在线程安全的问题,方法的作用是打印出字符串中的每一个字符,方法如下:
- public void output(String name) {
- int len = name.length();
- for (int i = 0; i < len; i++) {
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
线程A和线程B代码如下:(直接写在了init()方法中了)
- private void init() {
- outputer outputer = new outputer();
- new Thread(new Runnable() {
- @Override
- public void run() {
- while (true) {
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- outputer.output("songshengchao");
- }
- }
- }).start();
- new Thread(new Runnable() {
- @Override
- public void run() {
- while (true) {
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- outputer.output("songxiaochao");
- }
- }
- }).start();
- }
测试一下,肯定会出现线程不安全的问题,这是母庸质疑的事实,测试代码如下:
- public static void main(String[] args) {
- new TraditionalThreadSynchronized().init();
- }
三种解决办法,代码如下:
- public class outputer {
- public void output(String name) {
- int len = name.length();
- synchronized (this) { // 传进来当前调用方法的对象,要求线程用的是同一个对象
- //synchronized (outputer.class) { // 这样和outputer3方法达到线程互斥
- for (int i = 0; i < len; i++) {
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
- // 方法上的锁对象用的就是this当前对象
- public synchronized void output2(String name) {
- int len = name.length();
- for (int i = 0; i < len; i++) {
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- // output3 想和output方法达到线程互斥
- public static synchronized void output3(String name) {
- int len = name.length();
- for (int i = 0; i < len; i++) {
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
注意:至于第三种静态的synchronized方法,在和第一个方法共用时,第一种方法一定是用当前对象的class字节码文件,才能确保两个方法用的同一个对象。
二、线程互斥与通信的经典面试题
面试题:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着在主线程循环100次,如此循环50次,程序如何写???
经验之谈,设计思想,设计思路:
要用到共同数据(包括同步锁)的若干个方法应该归在同一个类上,这种设计正好提现了程序的高内聚与健壮性
思路:先写主线程与子线程的循环,然后在考虑轮流执行。先考虑循环,代码如下:
- public class TraditionalThreadCommunication {
- public static void main(String[] args) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- for (int i = 1; i <= 50; i++) {
- synchronized (TraditionalThreadCommunication.class) {
- for (int j = 1; j <= 10; j++) {
- System.out.println("sub thread sequece of" + j + ", loop of " + i);
- }
- }
- }
- }
- }).start();
- // 本身main方法就是主线程,直接可以写循环代码
- for (int i = 1; i <= 50; i++) {
- synchronized (TraditionalThreadCommunication.class) {
- for (int j = 1; j <= 100; j++) {
- System.out.println("main thread sequece of" + j + ", loop of " + i);
- }
- }
- }
- }
- }
代码优化,用面向对象的思想,将那些代码放到一个公共的类中,然后执行类中的不同方法,优化成一个公共的类,代码如下:
- public class Business {
- public synchronized void sub(int i){
- for (int j = 1; j <= 10; j++) {
- System.out.println("sub thread sequece of" + j + ", loop of " + i);
- }
- }
- public synchronized void main(int i){
- for (int j = 1; j <= 100; j++) {
- System.out.println("main thread sequece of" + j + ", loop of " + i);
- }
- }
- }
- ----------------------------------------------------------------------------------------------
- public class TraditionalThreadCommunication {
- public static void main(String[] args) {
- Business business = new Business();
- new Thread(new Runnable() {
- @Override
- public void run() {
- for (int i = 1; i <= 50; i++) {
- business.sub(i);
- }
- }
- }).start();
- // 本身main方法就是主线程,直接可以写循环代码
- for (int i = 1; i <= 50; i++) {
- business.main(i);
- }
- }
- }
最终的完整代码如下(详细注释):
- public class Business {
- // 是否是子线程执行 默认子线程先执行
- private boolean bShouldSub = true;
- public synchronized void sub(int i) {
- // 不是子线程应该执行 让给主线程 子线程执行等待的方法
- while (!bShouldSub) {
- try {
- this.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- for (int j = 1; j <= 10; j++) {
- System.out.println("sub thread sequece of" + j + ", loop of " + i);
- }
- // 子线程执行完毕后 让给主线程执行
- bShouldSub = false;
- // 唤醒主线程
- this.notify();
- }
- public synchronized void main(int i) {
- // 是子线程应该执行 让给子线程执行 主线程执行等待的方法
- while (bShouldSub) {
- try {
- this.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- for (int j = 1; j <= 100; j++) {
- System.out.println("main thread sequece of" + j + ", loop of " + i);
- }
- // 主线程执行费完毕后 交给子线程执行
- bShouldSub = true;
- // 唤醒子线程
- this.notify();
- }
- }
- ------------------------------------------------------------------------------------------------
- public class TraditionalThreadCommunication {
- public static void main(String[] args) {
- Business business = new Business();
- new Thread(new Runnable() {
- @Override
- public void run() {
- for (int i = 1; i <= 50; i++) {
- business.sub(i);
- }
- }
- }).start();
- // 本身main方法就是主线程,直接可以写循环代码
- for (int i = 1; i <= 50; i++) {
- business.main(i);
- }
- }
- }
Java中的线程--线程的互斥与同步通信的更多相关文章
- Java中的守护线程 & 非守护线程(简介)
Java中的守护线程 & 非守护线程 守护线程 (Daemon Thread) 非守护线程,又称用户线程(User Thread) 用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守 ...
- Java中如何创建线程
Java中如何创建线程 两种方式:1)继承Thread类:2)实现Runnable接口. 1.继承Thread类 继承Thread类,重写run方法,在run方法中定义需要执行的任务. class M ...
- 关于Java中进程和线程的详解
一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...
- Java中怎样创建线程安全的方法
面试问题: 下面的方法是否线程安全?怎样让它成为线程安全的方法? class MyCounter { private static int counter = 0; public static int ...
- ThreadLocal,Java中特殊的线程绑定机制
在DRP项目中,我们使用了ThreadLocal来创建Connection连接,避免了一直以参数的形式将Connection向下传递(传递connection的目的是由于jdbc事务要求确保使用同一个 ...
- Java中的守护线程和非守护线程(转载)
<什么是守护线程,什么是非守护线程> Java有两种Thread:"守护线程Daemon"(守护线程)与"用户线程User"(非守护线程). 用户线 ...
- Java中的守护线程
守护线程的概念 在java中有两种线程,守护线程和非守护线程,其两者并没有本质的区别,唯一的区别就是当前的用户线程退出的时候,若只存在唯一的A线程,若A线程为守护线程,那么JVM将会直接退出,否则JV ...
- (转)Java中的守护线程
Java的守护线程与非守护线程 守护线程与非守护线程 最近在看多线程的Timer章节,发现运用到了守护线程,感觉Java的基础知识还是需要补充. Java分为两种线程:用户线程和守护线程 所谓守护 ...
- Java中的守护线程——daemon
絮叨 Java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) 定义:守护线程(aka:服务线程),在没有用户线程可服务时会自动离开. 优先级:守护线程的优先级 ...
- Java-ThreadLocal,Java中特殊的线程绑定机制
在DRP项目中,我们使用了ThreadLocal来创建Connection连接,避免了一直以参数的形式将Connection向下传递(传递connection的目的是由于jdbc事务要求确保使用同一个 ...
随机推荐
- opencv3.1 压缩并拼图
必须有重叠才能拼,压缩越多,拼接越快 #include <opencv2\opencv.hpp> #include <opencv2\stitching.hpp> using ...
- 骨骼蒙皮动画(SkinnedMesh Animation)的实现
http://blog.csdn.net/zjull/article/details/11529695 1.简介 骨骼蒙皮动画,简称骨骼动画,因其占用磁盘空间少并且动画效果好被广泛用于3D游戏中,它把 ...
- ADT版本查看,This Android SDK requires Andr...ate ADT to the latest问题
ADT版本查看 Help->About ADT This Android SDK requires Andr...ate ADT to the latest问题 这样的问题很好解决,一个升级AD ...
- 即时通讯新手入门:一文读懂什么是Nginx?它能否实现IM的负载均衡?
本文引用了“蔷薇Nina”的“Nginx 相关介绍(Nginx是什么?能干嘛?)”一文部分内容,感谢作者的无私分享. 1.引言 Nginx(及其衍生产品)是目前被大量使用的服务端反向代理和负载均衡 ...
- C#Sqlite增删改查
说到使用数据库的话,无非也就是对数据的增加,删除和修改以及查询.前文已经 创建好了程序,现在我们就可以来具体实现Sqlite的数据操作,增删改查. 第一步,创建连接字符串来连接数据库: private ...
- react-native-wechat微信组件的使用
对我来说link没有成功过,所以参考了其他人的文章,原文:https://www.jianshu.com/p/6a792118fae4 第一步:要去:https://open.weixin.qq.co ...
- [!] No `Podfile' found in the project directory.
1.cd ios/ 2.vim Podfile(创建Podfile)且输入内容 source'https://github.com/CocoaPods/Specs.git'platform:ios,' ...
- JS时间框架之舍弃Moment.js拥抱Day.js
什么是Day.js Day.js 是一个轻量的处理时间和日期的 JavaScript 库,和 Moment.js 的 API 设计保持完全一样. 如果您曾经用过 Moment.js, 那么您已经知道如 ...
- 黑马Lambda表达式学习 Stream流 函数式接口 Lambda表达式 方法引用
- python异常之ModuleNotFoundError: No module named 'test01inner02'
当我们使用sys.path.append(args) 指令向程序中导入模块时其实本次append操作只是在内存中完成的,如果要永久性的添加需要修改环境变量. 我们发现当我们使用print(sys.pa ...