多线程温习2. 多线程之间的同步

举报
yd_57386892 发表于 2020/12/28 22:39:01 2020/12/28
【摘要】 现在我们要启动2个线程,第一个线程打印“gaoxiaowei”中的每一个字符,然后换行; 第二个线程打印“zhangwenbin”的每一个字符然后换行,代码如下: public class TraditionalThreadSynchronized { /** * @param args */ public static void main(String[] args...

现在我们要启动2个线程,第一个线程打印“gaoxiaowei”中的每一个字符,然后换行; 第二个线程打印“zhangwenbin”的每一个字符然后换行,代码如下:


  
  1. public class TraditionalThreadSynchronized {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. new TraditionalThreadSynchronized().init();
  7. }
  8. private void init(){
  9. final Outputer outputer = new Outputer();
  10. new Thread(new Runnable(){
  11. @Override
  12. public void run() {
  13. while(true){
  14. try {
  15. Thread.sleep(10);
  16. } catch (InterruptedException e) {
  17. // TODO Auto-generated catch block
  18. e.printStackTrace();
  19. }
  20. outputer.output("gaoxiaowei");
  21. }
  22. }
  23. }).start();
  24. new Thread(new Runnable(){
  25. @Override
  26. public void run() {
  27. while(true){
  28. try {
  29. Thread.sleep(10);
  30. } catch (InterruptedException e) {
  31. // TODO Auto-generated catch block
  32. e.printStackTrace();
  33. }
  34. outputer.output("zhangwenbin");
  35. }
  36. }
  37. }).start();
  38. }
  39. static class Outputer{
  40. public void output(String name){
  41. int len = name.length();
  42. for(int i=0;i<len;i++){
  43. System.out.print(name.charAt(i));
  44. }
  45. System.out.println();
  46. }
  47. public void output2(String name){
  48. int len = name.length();
  49. for(int i=0;i<len;i++){
  50. System.out.print(name.charAt(i));
  51. }
  52. System.out.println();
  53. }
  54. public static void output3(String name){
  55. int len = name.length();
  56. for(int i=0;i<len;i++){
  57. System.out.print(name.charAt(i));
  58. }
  59. System.out.println();
  60. }
  61. }
  62. }

我们预期的输出是:

gaoxiaowei

zhangwenbin

gaoxiaowei

gaoxiaowei

zhangwenbin

类似于这样,一个线程逐个打印完字符串中的每个字符后,第二个线程再开始打印第2个字符串中的每个字符。

可是,上述代码,打印出的字符串里包含如下的字符串:

gzhanaoxiaowei
gwenbin
gaoxiaowei
zhangwenbin
gaoxiaowei
zhangwenbin
zhangwengaoxibin
aowei
gazhangweoxnbin
iaowei
zhanggaoxiaoweiwenbin

这种是如何导致的呢?是因为这2个线程公用了一个电脑屏幕资源,我们的输出设备只有一个,但是抢占的线程有2个。当CPU时间片分 给线程1的时候它打印出了g,然后这是线程1挂起,CPU时间片轮到了线程2,这时线程2打印zhan到屏幕,接着又是线程1继续打印aoxiaowei剩下的字符。 这说明我们的2个线程并没有同步,而是不配合的抢占CPU资源,导致你输出你的,我输出我的,最终大家配合的不好,导致屏幕上的输出,谁也没得到甜处——完整的输出自己的字符串。

如何破解?需要加同步关键字synchronized,我们将这个关键字加到output函数上,代码如下:


  
  1. public synchronized 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. }

这下就好了,输出达到了预期。synchronized表示2个线程执行同一个output对象的output函数时,要排序执行,也就是说第1个线程执行完了,第2个线程才能开始,synchronized是一把同步锁,只有第1个线程执行完output函数后,锁子才能释放,别的线程才能继续执行outputer.output函数。

这时我们把第二个线程的 outputer.output改为output3. 现在的代码如下:


  
  1. public class TraditionalThreadSynchronized {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. new TraditionalThreadSynchronized().init();
  7. }
  8. private void init(){
  9. final Outputer outputer = new Outputer();
  10. final Outputer outputer2 = new Outputer();
  11. new Thread(new Runnable(){
  12. @Override
  13. public void run() {
  14. while(true){
  15. try {
  16. Thread.sleep(10);
  17. } catch (InterruptedException e) {
  18. // TODO Auto-generated catch block
  19. e.printStackTrace();
  20. }
  21. outputer.output("gaoxiaowei");
  22. }
  23. }
  24. }).start();
  25. new Thread(new Runnable(){
  26. @Override
  27. public void run() {
  28. while(true){
  29. try {
  30. Thread.sleep(10);
  31. } catch (InterruptedException e) {
  32. // TODO Auto-generated catch block
  33. e.printStackTrace();
  34. }
  35. outputer. output3("zhangwenbin");
  36. }
  37. }
  38. }).start();
  39. }
  40. static class Outputer{
  41. public synchronized void output(String name){
  42. int len = name.length();
  43. for(int i=0;i<len;i++){
  44. System.out.print(name.charAt(i));
  45. }
  46. System.out.println();
  47. }
  48. public void output2(String name){
  49. int len = name.length();
  50. for(int i=0;i<len;i++){
  51. System.out.print(name.charAt(i));
  52. }
  53. System.out.println();
  54. }
  55. public static void output3(String name){
  56. int len = name.length();
  57. for(int i=0;i<len;i++){
  58. System.out.print(name.charAt(i));
  59. }
  60. System.out.println();
  61. }
  62. }

试问,输出还能不错乱吗,能达到预期吗?

答案是不能达到预期,会错乱!因为当线程1执行output的时候,synchronized只对output上了锁,而output3并没有上锁。也就说,即使线程1没有执行完output函数时,没有释放锁的情况下,线程2照样可以执行output3函数来抢占屏幕资源。

 

如何破解?给output3也加锁synchronized关键字,实际上output与output3用的同步锁都为同一个锁:当前对象this。因此当线程1执行output的中途,this这个同步锁没有释放,那么线程2就被禁止执行output3,直到线程1执行完output函数并释放锁。

改正后的代码如下:


  
  1. public class TraditionalThreadSynchronized {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. new TraditionalThreadSynchronized().init();
  7. }
  8. private void init(){
  9. final Outputer outputer = new Outputer();
  10. final Outputer outputer2 = new Outputer();
  11. new Thread(new Runnable(){
  12. @Override
  13. public void run() {
  14. while(true){
  15. try {
  16. Thread.sleep(10);
  17. } catch (InterruptedException e) {
  18. // TODO Auto-generated catch block
  19. e.printStackTrace();
  20. }
  21. outputer.output("gaoxiaowei");
  22. }
  23. }
  24. }).start();
  25. new Thread(new Runnable(){
  26. @Override
  27. public void run() {
  28. while(true){
  29. try {
  30. Thread.sleep(10);
  31. } catch (InterruptedException e) {
  32. // TODO Auto-generated catch block
  33. e.printStackTrace();
  34. }
  35. outputer. output3("zhangwenbin");
  36. }
  37. }
  38. }).start();
  39. }
  40. static class Outputer{
  41. public synchronized void output(String name){
  42. int len = name.length();
  43. for(int i=0;i<len;i++){
  44. System.out.print(name.charAt(i));
  45. }
  46. System.out.println();
  47. }
  48. public void output2(String name){
  49. int len = name.length();
  50. for(int i=0;i<len;i++){
  51. System.out.print(name.charAt(i));
  52. }
  53. System.out.println();
  54. }
  55. public synchronized void output3(String name){
  56. int len = name.length();
  57. for(int i=0;i<len;i++){
  58. System.out.print(name.charAt(i));
  59. }
  60. System.out.println();
  61. }
  62. }
  63. }

接下来,把output函数改为static类型:


  
  1. public static synchronized 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. }

这时线程1的output与线程2的output3会同步吗?

答案:不会。

因为静态函数output不专属于某一个对象,它是属于整个类的。因此output3的synchronized同步锁,锁不住output的执行。

如何破解?

方法1:把output3也变为static类型,


  
  1. public static synchronized void output3(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. }

这时output和output3的synchronized锁就指向一个共同的锁了:类的字节码Outputer.class

方法2:现在output函数是static的类型,那么它的锁实质就是synchronized(Outputer.class); 那么,我们可以把output3的同步锁也改成字节码:


  
  1. public void output3(String name){
  2. synchronized(Outputer.class)
  3. {
  4. int len = name.length();
  5. for(int i=0;i<len;i++){
  6. System.out.print(name.charAt(i));
  7. }
  8. System.out.println();
  9. }
  10. }

这时output与output3拥有了同一个锁,这下输出就正常了。总之,要想同步使用临界资源:屏幕。为了保证按顺序打印,就得为各个output函数上同一把锁,切记是同一把。最后的代码如下:


  
  1. public class TraditionalThreadSynchronized {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. new TraditionalThreadSynchronized().init();
  7. }
  8. private void init(){
  9. final Outputer outputer = new Outputer();
  10. final Outputer outputer2 = new Outputer();
  11. new Thread(new Runnable(){
  12. @Override
  13. public void run() {
  14. while(true){
  15. try {
  16. Thread.sleep(10);
  17. } catch (InterruptedException e) {
  18. // TODO Auto-generated catch block
  19. e.printStackTrace();
  20. }
  21. outputer.output("gaoxiaowei");
  22. }
  23. }
  24. }).start();
  25. new Thread(new Runnable(){
  26. @Override
  27. public void run() {
  28. while(true){
  29. try {
  30. Thread.sleep(10);
  31. } catch (InterruptedException e) {
  32. // TODO Auto-generated catch block
  33. e.printStackTrace();
  34. }
  35. outputer.output3("zhangwenbin");
  36. }
  37. }
  38. }).start();
  39. }
  40. static class Outputer{
  41. public static synchronized void output(String name){
  42. int len = name.length();
  43. for(int i=0;i<len;i++){
  44. System.out.print(name.charAt(i));
  45. }
  46. System.out.println();
  47. }
  48. public void output2(String name){
  49. int len = name.length();
  50. for(int i=0;i<len;i++){
  51. System.out.print(name.charAt(i));
  52. }
  53. System.out.println();
  54. }
  55. public void output3(String name){
  56. synchronized(Outputer.class)
  57. {
  58. int len = name.length();
  59. for(int i=0;i<len;i++){
  60. System.out.print(name.charAt(i));
  61. }
  62. System.out.println();
  63. }
  64. }
  65. }

 

思考:现在我们把线程2更改为outputer2.output2, 注意是outputer2对象的output2函数,.对象变了。我们为output2函数也加把锁synchronized, 现在的代码如下:


  
  1. public class TraditionalThreadSynchronized {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. new TraditionalThreadSynchronized().init();
  7. }
  8. private void init(){
  9. final Outputer outputer = new Outputer();
  10. final Outputer outputer2 = new Outputer();
  11. new Thread(new Runnable(){
  12. @Override
  13. public void run() {
  14. while(true){
  15. try {
  16. Thread.sleep(10);
  17. } catch (InterruptedException e) {
  18. // TODO Auto-generated catch block
  19. e.printStackTrace();
  20. }
  21. outputer.output("gaoxiaowei");
  22. }
  23. }
  24. }).start();
  25. new Thread(new Runnable(){
  26. @Override
  27. public void run() {
  28. while(true){
  29. try {
  30. Thread.sleep(10);
  31. } catch (InterruptedException e) {
  32. // TODO Auto-generated catch block
  33. e.printStackTrace();
  34. }
  35. outputer2.output2("zhangwenbin");
  36. }
  37. }
  38. }).start();
  39. }
  40. static class Outputer{
  41. public static synchronized void output(String name){
  42. int len = name.length();
  43. for(int i=0;i<len;i++){
  44. System.out.print(name.charAt(i));
  45. }
  46. System.out.println();
  47. }
  48. public synchronized void output2(String name){
  49. int len = name.length();
  50. for(int i=0;i<len;i++){
  51. System.out.print(name.charAt(i));
  52. }
  53. System.out.println();
  54. }
  55. public void output3(String name){
  56. synchronized(Outputer.class)
  57. {
  58. int len = name.length();
  59. for(int i=0;i<len;i++){
  60. System.out.print(name.charAt(i));
  61. }
  62. System.out.println();
  63. }
  64. }
  65. }

输出能同步吗?

答案:不能,因为outputer.output用的锁是 类的字节码Outputer.class,而outputer2.output2函数的synchronized 指的是output2.this这个对象。它们俩不是同一把锁,因此最终还会对屏幕进行错乱输出,抢占资源时发生冲突。

 

 

 

文章来源: blog.csdn.net,作者:冉航--小虾米,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/gaoxiaoweiandy/article/details/106122756

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。