Java中的线程之前也提到过,但是还是想再详细的学习一下,跟着张孝祥老师,系统的再学习一下。

一、线程中的互斥

线程安全中的问题解释:线程安全问题可以用银行中的转账

例题描述:

线程A与线程B分别访问同一个对象的方法,这样就会存在线程安全的问题,方法的作用是打印出字符串中的每一个字符,方法如下:

  1. public void output(String name) {
  2. int len = name.length();
  3. for (int i = 0; i < len; i++) {
  4. System.out.print(name.charAt(i));
  5. }
  6. System.out.println();
  7. }

线程A和线程B代码如下:(直接写在了init()方法中了)

  1. private void init() {
  2. outputer outputer = new outputer();
  3. new Thread(new Runnable() {
  4. @Override
  5. public void run() {
  6. while (true) {
  7. try {
  8. Thread.sleep(10);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. outputer.output("songshengchao");
  13. }
  14. }
  15. }).start();
  16.  
  17. new Thread(new Runnable() {
  18. @Override
  19. public void run() {
  20. while (true) {
  21. try {
  22. Thread.sleep(10);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. outputer.output("songxiaochao");
  27. }
  28. }
  29. }).start();
  30. }

测试一下,肯定会出现线程不安全的问题,这是母庸质疑的事实,测试代码如下:

  1. public static void main(String[] args) {
  2. new TraditionalThreadSynchronized().init();
  3. }

三种解决办法,代码如下:

  1. public class outputer {
  2. public void output(String name) {
  3. int len = name.length();
  4. synchronized (this) { // 传进来当前调用方法的对象,要求线程用的是同一个对象
  5. //synchronized (outputer.class) { // 这样和outputer3方法达到线程互斥
  6. for (int i = 0; i < len; i++) {
  7. System.out.print(name.charAt(i));
  8. }
  9. System.out.println();
  10. }
  11. }
  12.  
  13. // 方法上的锁对象用的就是this当前对象
  14. public synchronized void output2(String name) {
  15. int len = name.length();
  16. for (int i = 0; i < len; i++) {
  17. System.out.print(name.charAt(i));
  18. }
  19. System.out.println();
  20. }
  21.  
  22. // output3 想和output方法达到线程互斥
  23. public static synchronized void output3(String name) {
  24. int len = name.length();
  25. for (int i = 0; i < len; i++) {
  26. System.out.print(name.charAt(i));
  27. }
  28. System.out.println();
  29. }
  30. }

注意:至于第三种静态的synchronized方法,在和第一个方法共用时,第一种方法一定是用当前对象的class字节码文件,才能确保两个方法用的同一个对象。

二、线程互斥与通信的经典面试题

面试题:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着在主线程循环100次,如此循环50次,程序如何写???

经验之谈,设计思想,设计思路:

要用到共同数据(包括同步锁)的若干个方法应该归在同一个类上,这种设计正好提现了程序的高内聚与健壮性

思路:先写主线程与子线程的循环,然后在考虑轮流执行。先考虑循环,代码如下:

  1. public class TraditionalThreadCommunication {
  2.  
  3. public static void main(String[] args) {
  4.  
  5. new Thread(new Runnable() {
  6.  
  7. @Override
  8. public void run() {
  9. for (int i = 1; i <= 50; i++) {
  10. synchronized (TraditionalThreadCommunication.class) {
  11. for (int j = 1; j <= 10; j++) {
  12. System.out.println("sub thread sequece of" + j + ", loop of " + i);
  13. }
  14. }
  15.  
  16. }
  17. }
  18. }).start();
  19.  
  20. // 本身main方法就是主线程,直接可以写循环代码
  21. for (int i = 1; i <= 50; i++) {
  22. synchronized (TraditionalThreadCommunication.class) {
  23. for (int j = 1; j <= 100; j++) {
  24. System.out.println("main thread sequece of" + j + ", loop of " + i);
  25. }
  26. }
  27. }
  28.  
  29. }
  30. }

代码优化,用面向对象的思想,将那些代码放到一个公共的类中,然后执行类中的不同方法,优化成一个公共的类,代码如下:

  1. public class Business {
  2.  
  3. public synchronized void sub(int i){
  4. for (int j = 1; j <= 10; j++) {
  5. System.out.println("sub thread sequece of" + j + ", loop of " + i);
  6. }
  7. }
  8.  
  9. public synchronized void main(int i){
  10. for (int j = 1; j <= 100; j++) {
  11. System.out.println("main thread sequece of" + j + ", loop of " + i);
  12. }
  13. }
  14. }
  15.  
  16. ----------------------------------------------------------------------------------------------
  17.  
  18. public class TraditionalThreadCommunication {
  19.  
  20. public static void main(String[] args) {
  21. Business business = new Business();
  22. new Thread(new Runnable() {
  23.  
  24. @Override
  25. public void run() {
  26. for (int i = 1; i <= 50; i++) {
  27. business.sub(i);
  28. }
  29. }
  30. }).start();
  31.  
  32. // 本身main方法就是主线程,直接可以写循环代码
  33. for (int i = 1; i <= 50; i++) {
  34. business.main(i);
  35. }
  36.  
  37. }
  38.  
  39. }

最终的完整代码如下(详细注释):

  1. public class Business {
  2.  
  3. // 是否是子线程执行 默认子线程先执行
  4. private boolean bShouldSub = true;
  5.  
  6. public synchronized void sub(int i) {
  7. // 不是子线程应该执行 让给主线程 子线程执行等待的方法
  8. while (!bShouldSub) {
  9. try {
  10. this.wait();
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. for (int j = 1; j <= 10; j++) {
  16. System.out.println("sub thread sequece of" + j + ", loop of " + i);
  17. }
  18. // 子线程执行完毕后 让给主线程执行
  19. bShouldSub = false;
  20. // 唤醒主线程
  21. this.notify();
  22. }
  23.  
  24. public synchronized void main(int i) {
  25. // 是子线程应该执行 让给子线程执行 主线程执行等待的方法
  26. while (bShouldSub) {
  27. try {
  28. this.wait();
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. for (int j = 1; j <= 100; j++) {
  34. System.out.println("main thread sequece of" + j + ", loop of " + i);
  35. }
  36. // 主线程执行费完毕后 交给子线程执行
  37. bShouldSub = true;
  38. // 唤醒子线程
  39. this.notify();
  40. }
  41. }
  42. ------------------------------------------------------------------------------------------------
  43.  
  44. public class TraditionalThreadCommunication {
  45.  
  46. public static void main(String[] args) {
  47. Business business = new Business();
  48. new Thread(new Runnable() {
  49.  
  50. @Override
  51. public void run() {
  52. for (int i = 1; i <= 50; i++) {
  53. business.sub(i);
  54. }
  55. }
  56. }).start();
  57.  
  58. // 本身main方法就是主线程,直接可以写循环代码
  59. for (int i = 1; i <= 50; i++) {
  60. business.main(i);
  61. }
  62.  
  63. }
  64.  
  65. }

Java中的线程--线程的互斥与同步通信的更多相关文章

  1. Java中的守护线程 & 非守护线程(简介)

    Java中的守护线程 & 非守护线程 守护线程 (Daemon Thread) 非守护线程,又称用户线程(User Thread) 用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守 ...

  2. Java中如何创建线程

    Java中如何创建线程 两种方式:1)继承Thread类:2)实现Runnable接口. 1.继承Thread类 继承Thread类,重写run方法,在run方法中定义需要执行的任务. class M ...

  3. 关于Java中进程和线程的详解

    一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...

  4. Java中怎样创建线程安全的方法

    面试问题: 下面的方法是否线程安全?怎样让它成为线程安全的方法? class MyCounter { private static int counter = 0; public static int ...

  5. ThreadLocal,Java中特殊的线程绑定机制

    在DRP项目中,我们使用了ThreadLocal来创建Connection连接,避免了一直以参数的形式将Connection向下传递(传递connection的目的是由于jdbc事务要求确保使用同一个 ...

  6. Java中的守护线程和非守护线程(转载)

    <什么是守护线程,什么是非守护线程> Java有两种Thread:"守护线程Daemon"(守护线程)与"用户线程User"(非守护线程). 用户线 ...

  7. Java中的守护线程

    守护线程的概念 在java中有两种线程,守护线程和非守护线程,其两者并没有本质的区别,唯一的区别就是当前的用户线程退出的时候,若只存在唯一的A线程,若A线程为守护线程,那么JVM将会直接退出,否则JV ...

  8. (转)Java中的守护线程

    Java的守护线程与非守护线程   守护线程与非守护线程 最近在看多线程的Timer章节,发现运用到了守护线程,感觉Java的基础知识还是需要补充. Java分为两种线程:用户线程和守护线程 所谓守护 ...

  9. Java中的守护线程——daemon

    絮叨 Java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) 定义:守护线程(aka:服务线程),在没有用户线程可服务时会自动离开. 优先级:守护线程的优先级 ...

  10. Java-ThreadLocal,Java中特殊的线程绑定机制

    在DRP项目中,我们使用了ThreadLocal来创建Connection连接,避免了一直以参数的形式将Connection向下传递(传递connection的目的是由于jdbc事务要求确保使用同一个 ...

随机推荐

  1. opencv3.1 压缩并拼图

    必须有重叠才能拼,压缩越多,拼接越快 #include <opencv2\opencv.hpp> #include <opencv2\stitching.hpp> using ...

  2. 骨骼蒙皮动画(SkinnedMesh Animation)的实现

    http://blog.csdn.net/zjull/article/details/11529695 1.简介 骨骼蒙皮动画,简称骨骼动画,因其占用磁盘空间少并且动画效果好被广泛用于3D游戏中,它把 ...

  3. 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 ...

  4. 即时通讯新手入门:一文读懂什么是Nginx?它能否实现IM的负载均衡?

    本文引用了“蔷薇Nina”的“Nginx 相关介绍(Nginx是什么?能干嘛?)”一文部分内容,感谢作者的无私分享. 1.引言   Nginx(及其衍生产品)是目前被大量使用的服务端反向代理和负载均衡 ...

  5. C#Sqlite增删改查

    说到使用数据库的话,无非也就是对数据的增加,删除和修改以及查询.前文已经 创建好了程序,现在我们就可以来具体实现Sqlite的数据操作,增删改查. 第一步,创建连接字符串来连接数据库: private ...

  6. react-native-wechat微信组件的使用

    对我来说link没有成功过,所以参考了其他人的文章,原文:https://www.jianshu.com/p/6a792118fae4 第一步:要去:https://open.weixin.qq.co ...

  7. [!] No `Podfile' found in the project directory.

    1.cd ios/ 2.vim Podfile(创建Podfile)且输入内容 source'https://github.com/CocoaPods/Specs.git'platform:ios,' ...

  8. JS时间框架之舍弃Moment.js拥抱Day.js

    什么是Day.js Day.js 是一个轻量的处理时间和日期的 JavaScript 库,和 Moment.js 的 API 设计保持完全一样. 如果您曾经用过 Moment.js, 那么您已经知道如 ...

  9. 黑马Lambda表达式学习 Stream流 函数式接口 Lambda表达式 方法引用

  10. python异常之ModuleNotFoundError: No module named 'test01inner02'

    当我们使用sys.path.append(args) 指令向程序中导入模块时其实本次append操作只是在内存中完成的,如果要永久性的添加需要修改环境变量. 我们发现当我们使用print(sys.pa ...