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

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

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


      public class TraditionalThreadSynchronized {
     	/**
       * @param args
       */
     	public static void main(String[] args) {
     		new TraditionalThreadSynchronized().init();
      	}
     	private void init(){
     		final Outputer outputer = new Outputer();
     		new Thread(new Runnable(){
     			@Override
     			public void run() {
      while(true){
      try {
       Thread.sleep(10);
       } catch (InterruptedException e) {
      // TODO Auto-generated catch block
       e.printStackTrace();
       }
       outputer.output("gaoxiaowei");
       }
      			}
      		}).start();
     		new Thread(new Runnable(){
     			@Override
     			public void run() {
      while(true){
      try {
       Thread.sleep(10);
       } catch (InterruptedException e) {
      // TODO Auto-generated catch block
       e.printStackTrace();
       }
       outputer.output("zhangwenbin");
       }
      			}
      		}).start();
      	}
     	static class Outputer{
     		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();
      		}
     		public void output2(String name){
     			int len = name.length();
     			for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
      			}
      			System.out.println();
      		}
     		public static void output3(String name){
     			int len = name.length();
     			for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
      			}
      			System.out.println();
      		}
      	}
      }
  
 

我们预期的输出是:

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函数上,代码如下:


     	public synchronized void output(String name){
     			int len = name.length();
      for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
       }
       System.out.println();
      		}
  
 

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

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


      public class TraditionalThreadSynchronized {
     	/**
       * @param args
       */
     	public static void main(String[] args) {
     		new TraditionalThreadSynchronized().init();
      	}
     	private void init(){
     		final Outputer outputer = new Outputer();
     		final Outputer outputer2 = new Outputer();
     		new Thread(new Runnable(){
     			@Override
     			public void run() {
      while(true){
      try {
       Thread.sleep(10);
       } catch (InterruptedException e) {
      // TODO Auto-generated catch block
       e.printStackTrace();
       }
       outputer.output("gaoxiaowei");
       }
      			}
      		}).start();
     		new Thread(new Runnable(){
     			@Override
     			public void run() {
      while(true){
      try {
       Thread.sleep(10);
       } catch (InterruptedException e) {
      // TODO Auto-generated catch block
       e.printStackTrace();
       }
       outputer. output3("zhangwenbin");
       }
      			}
      		}).start();
      	}
     	static class Outputer{
     		public synchronized void output(String name){
     			int len = name.length();
      for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
       }
       System.out.println();
      		}
     		public void output2(String name){
     			int len = name.length();
     			for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
      			}
      			System.out.println();
      		}
     		public static void output3(String name){
     			int len = name.length();
     			for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
      			}
      			System.out.println();
      		}
      	}
  
 

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

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

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

改正后的代码如下:


      public class TraditionalThreadSynchronized {
     	/**
       * @param args
       */
     	public static void main(String[] args) {
     		new TraditionalThreadSynchronized().init();
      	}
     	private void init(){
     		final Outputer outputer = new Outputer();
     		final Outputer outputer2 = new Outputer();
     		new Thread(new Runnable(){
     			@Override
     			public void run() {
      while(true){
      try {
       Thread.sleep(10);
       } catch (InterruptedException e) {
      // TODO Auto-generated catch block
       e.printStackTrace();
       }
       outputer.output("gaoxiaowei");
       }
      			}
      		}).start();
     		new Thread(new Runnable(){
     			@Override
     			public void run() {
      while(true){
      try {
       Thread.sleep(10);
       } catch (InterruptedException e) {
      // TODO Auto-generated catch block
       e.printStackTrace();
       }
       outputer. output3("zhangwenbin");
       }
      			}
      		}).start();
      	}
     	static class Outputer{
     		public synchronized void output(String name){
     			int len = name.length();
      for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
       }
       System.out.println();
      		}
     		public void output2(String name){
     			int len = name.length();
     			for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
      			}
      			System.out.println();
      		}
     		public 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();
      		}
      	}
      }
  
 

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


     	public static synchronized void output(String name){
     			int len = name.length();
      for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
       }
       System.out.println();
      		}
  
 

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

答案:不会。

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

如何破解?

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


     	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();
      		}
  
 

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

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


     		public void output3(String name){
     			synchronized(Outputer.class)
      			{
      int len = name.length();
      for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
       }
       System.out.println();
      			}
      		}
  
 

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


      public class TraditionalThreadSynchronized {
     	/**
       * @param args
       */
     	public static void main(String[] args) {
     		new TraditionalThreadSynchronized().init();
      	}
     	private void init(){
     		final Outputer outputer = new Outputer();
     		final Outputer outputer2 = new Outputer();
     		new Thread(new Runnable(){
      			@Override
     			public void run() {
      while(true){
      try {
       Thread.sleep(10);
       } catch (InterruptedException e) {
      // TODO Auto-generated catch block
       e.printStackTrace();
       }
       outputer.output("gaoxiaowei");
       }
      			}
      		}).start();
     		new Thread(new Runnable(){
      			@Override
     			public void run() {
      while(true){
      try {
       Thread.sleep(10);
       } catch (InterruptedException e) {
      // TODO Auto-generated catch block
       e.printStackTrace();
       }
       outputer.output3("zhangwenbin");
       }
      			}
      		}).start();
      	}
     	static class Outputer{
     		public  static synchronized void output(String name){
     			int len = name.length();
      for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
       }
       System.out.println();
      		}
     		public void output2(String name){
     			int len = name.length();
     			for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
      			}
      			System.out.println();
      		}
     		public void output3(String name){
      			synchronized(Outputer.class)
      			{
      int len = name.length();
      for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
       }
       System.out.println();
      			}
      		}
      	}
  
 

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


      public class TraditionalThreadSynchronized {
     	/**
       * @param args
       */
     	public static void main(String[] args) {
     		new TraditionalThreadSynchronized().init();
      	}
     	private void init(){
     		final Outputer outputer = new Outputer();
     		final Outputer outputer2 = new Outputer();
     		new Thread(new Runnable(){
     			@Override
     			public void run() {
      while(true){
      try {
       Thread.sleep(10);
       } catch (InterruptedException e) {
      // TODO Auto-generated catch block
       e.printStackTrace();
       }
       outputer.output("gaoxiaowei");
       }
      			}
      		}).start();
     		new Thread(new Runnable(){
     			@Override
     			public void run() {
      while(true){
      try {
       Thread.sleep(10);
       } catch (InterruptedException e) {
      // TODO Auto-generated catch block
       e.printStackTrace();
       }
       outputer2.output2("zhangwenbin");
       }
      			}
      		}).start();
      	}
     	static class Outputer{
     		public static synchronized void output(String name){
     			int len = name.length();
      for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
       }
       System.out.println();
      		}
     		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();
      		}
     		public void output3(String name){
     			synchronized(Outputer.class)
      			{
      int len = name.length();
      for(int i=0;i<len;i++){
       System.out.print(name.charAt(i));
       }
       System.out.println();
      			}
      		}
      	}
  
 

输出能同步吗?

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

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

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

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

作者其他文章

评论(0

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

    全部回复

    上滑加载中

    设置昵称

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

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

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