Java-多线程同步疑问

隐语者 2019-05-15 02:17:03
问题:我有一个想法想要实现,例如:当我自己做一个API让别人用时,别人会有很多东西会传过来,我的业务逻辑会一个个处理,但这个务必会占用很多时间,我就想着能不能把一个个业务逻辑分配给多线程去处理,就有了如下简单的代码:


private static Integer lt_num1=0;

private static Integer lt_num2=0;

public static void main(String[] args) throws Exception {
ExecutorService Service=Executors.newFixedThreadPool(20);
long begin_time,end_time;
begin_time=System.currentTimeMillis();
for(int i=1;i<=20;i++) {
lt_num2=i;
Service.submit(new Thread(new Runnable() {
@Override
public void run() {
num(lt_num2);
}
}));
}
end_time=System.currentTimeMillis();
System.out.println("Occupation time-->"+(end_time-begin_time));
}

private synchronized static void num(int lt_1){
lt_num1=lt_1;
// Thread.sleep(1500);
System.err.println(lt_num1);
}


这些代码就产生如下随机的结果:



我的想法是,1~20都可以正常打印出来,可以不按顺序,但是一定不可以重复。

请问各位大神,有没有好的建议,帮助下初学者。不甚感激。
...全文
99 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
隐语者 2019-05-15
  • 打赏
  • 举报
回复
引用 8 楼 maradona1984 的回复:
[quote=引用 4 楼 qq_40674493 的回复:] num这个方法有synchronized关键字修饰,看似线程安全的,但是方法中调用了,System.out.println();而out的锁是PrintStream的实例,和num方法不是一个锁,所以多个线程可以同时访问,这就出现了线程安全问题。你的赋值是安全的,但是out输出lt_num1时就不安全了。 有可能在out时lt_num1的值刚好被你的其它线程修改了,所以输出结果大部分重复的。是System.out.println();影响了输出结果。不是你的代码自身,而是调用System.out.println();失去了线程安全。
你这是误人子弟,同步块肯定没有线程安全问题 问题在于调用num(lt_num2);lt_num2 copy引用时(调用方法都会将参数复制一份引用/值传递到调用方法)主线程不断修改lt_num2的指向,如果多个子线程在下个修改之前成功复制了引用就会导致打印同样的值,你可以写代码测试一把[/quote] 主线程不断修改lt_num2的指向,如果多个子线程在下个修改之前成功复制了引用就会导致打印同样的值; 您说的这句话我瞬间懂了,茅舍顿来,非常感谢,也感谢其他人的慷慨解答,谢谢。
隐语者 2019-05-15
  • 打赏
  • 举报
回复
引用 3 楼 maradona1984 的回复:
[quote=引用 2 楼 隐语者 的回复:] [quote=引用 1 楼 maradona1984 的回复:] lt_num1这个变量不需要 lt_num2定义到for循环里面 也就是,你对多线程的理解很模糊

      public static void main(String[] args) throws Exception {
    	  ExecutorService Service=Executors.newFixedThreadPool(20);
    	  long begin_time,end_time;
    	  begin_time=System.currentTimeMillis();
    	  for(int i=1;i<=20;i++) {
    		 final Integer lt_num2=i;
    		  Service.submit(new Thread(new Runnable() {
    				  @Override
    				  public void run() {
						num(lt_num2);
    				  }
    			}));
    	  }
    	  end_time=System.currentTimeMillis();
    	  System.out.println("Occupation time-->"+(end_time-begin_time));
      }

      private synchronized static void num(int lt_1){
    	  System.err.println(lt_1);
      }

您好,我试了你的方法,确实可行,现在产生了两种疑问: 1、我的程序业务逻辑,那个“lt_num2”必须使用静态变量,因为会有别的类会对这个参数进行共享,为什么使用静态参数进行赋值会产生重复数据,使用局部变量进行赋值就不会产生重复? 2、你看你在局部变量“lt_num2”使用了final,我觉得可以去掉final(限制变量不可重赋值)也是可行的,是否有其他情况会产生让必须使用final修饰这个局部变量 请解析下,初学者不甚感激。 [/quote] 1.这也是为啥我说你对多线程很模糊的原因,这个你还是自己思考的好,说不清,你脑子里必须有对代码执行的顺序有清晰的认识,我只能提醒你,同一个线程里的代码是顺序执行的,不同线程里的执行是乱序的(这句话可能会让你产生错误的理解,懒得解释),而且你原来的代码违反常规套路,懒得去想,简单的东西被你搞得复杂,想着累 2.你把final删掉能编译通过?我也不知道你用的什么jdk版本,7和7以下匿名类访问局部变量应该必须加final修饰的,jdk8做了优化罢了,但实际上编译时加了final修饰的 [/quote] 主线程不断修改lt_num2的指向,如果多个子线程在下个修改之前成功复制了引用就会导致打印同样的值; 您说的这句话我瞬间懂了,茅舍顿来,非常感谢,也感谢其他人的慷慨解答,谢谢。
qq_40674493 2019-05-15
  • 打赏
  • 举报
回复
按你的需要,我想半天只有一种办法,就是让类成员变量的赋值和函数传参捆绑为一个原子操作。但是好像JAVA没这种功能吧。
qq_40674493 2019-05-15
  • 打赏
  • 举报
回复
不是局部变量做到的,是每次都创建了新的lt_num2变量,for循环每次都是对新的lt_num2赋值。for循环创建了20个lt_num2,每个线程使用1个,各是各的。当然不会被修改。
隐语者 2019-05-15
  • 打赏
  • 举报
回复
引用 3 楼 maradona1984 的回复:
[quote=引用 2 楼 隐语者 的回复:] [quote=引用 1 楼 maradona1984 的回复:] lt_num1这个变量不需要 lt_num2定义到for循环里面 也就是,你对多线程的理解很模糊

      public static void main(String[] args) throws Exception {
    	  ExecutorService Service=Executors.newFixedThreadPool(20);
    	  long begin_time,end_time;
    	  begin_time=System.currentTimeMillis();
    	  for(int i=1;i<=20;i++) {
    		 final Integer lt_num2=i;
    		  Service.submit(new Thread(new Runnable() {
    				  @Override
    				  public void run() {
						num(lt_num2);
    				  }
    			}));
    	  }
    	  end_time=System.currentTimeMillis();
    	  System.out.println("Occupation time-->"+(end_time-begin_time));
      }

      private synchronized static void num(int lt_1){
    	  System.err.println(lt_1);
      }

您好,我试了你的方法,确实可行,现在产生了两种疑问: 1、我的程序业务逻辑,那个“lt_num2”必须使用静态变量,因为会有别的类会对这个参数进行共享,为什么使用静态参数进行赋值会产生重复数据,使用局部变量进行赋值就不会产生重复? 2、你看你在局部变量“lt_num2”使用了final,我觉得可以去掉final(限制变量不可重赋值)也是可行的,是否有其他情况会产生让必须使用final修饰这个局部变量 请解析下,初学者不甚感激。 [/quote] 1.这也是为啥我说你对多线程很模糊的原因,这个你还是自己思考的好,说不清,你脑子里必须有对代码执行的顺序有清晰的认识,我只能提醒你,同一个线程里的代码是顺序执行的,不同线程里的执行是乱序的(这句话可能会让你产生错误的理解,懒得解释),而且你原来的代码违反常规套路,懒得去想,简单的东西被你搞得复杂,想着累 2.你把final删掉能编译通过?我也不知道你用的什么jdk版本,7和7以下匿名类访问局部变量应该必须加final修饰的,jdk8做了优化罢了,但实际上编译时加了final修饰的 [/quote] 你好,感谢您的解答,不甚感激。 我一直用的JDK是8.0+,所以没有一直没有遇到这个问题,刚去查了一下,确实8.0+对这个不加final加了优化,我是初学者,刚接触Java不久,让你见笑了。 至于 1、我为什么不用局部变量去放那个“i”的原因是,我的业余逻辑之后可能会在其他类使用到这个“lt_num2”,所以只把他设计成类静态成员变量,用之后来共享数据用的。 2、至于多线程之间的执行顺序是随机的这我知道,采用的是“资源抢占式”执行。谁先执行并不确定, 3、其实我的问题也很简单,局部变量赋值不用产生重复,类成员变量却产生了重复,这很奇怪,如果可以,请帮忙解答一下。不甚感激。
qq_40674493 2019-05-15
  • 打赏
  • 举报
回复
引用 8 楼 maradona1984 的回复:
[quote=引用 4 楼 qq_40674493 的回复:]
num这个方法有synchronized关键字修饰,看似线程安全的,但是方法中调用了,System.out.println();而out的锁是PrintStream的实例,和num方法不是一个锁,所以多个线程可以同时访问,这就出现了线程安全问题。你的赋值是安全的,但是out输出lt_num1时就不安全了。
有可能在out时lt_num1的值刚好被你的其它线程修改了,所以输出结果大部分重复的。是System.out.println();影响了输出结果。不是你的代码自身,而是调用System.out.println();失去了线程安全。

你这是误人子弟,同步块肯定没有线程安全问题
问题在于调用num(lt_num2);lt_num2 copy引用时(调用方法都会将参数复制一份引用/值传递到调用方法)主线程不断修改lt_num2的指向,如果多个子线程在下个修改之前成功复制了引用就会导致打印同样的值,你可以写代码测试一把[/quote]
确实,我这段说法错了。发出去才反应过来,分析错了。抱歉。
qq_40674493 2019-05-15
  • 打赏
  • 举报
回复
lt_num2只是其他类要用到,可以用传参的方式使用。多线程访问好像做不到lt_num2的安全。
maradona1984 2019-05-15
  • 打赏
  • 举报
回复
引用 4 楼 qq_40674493 的回复:
num这个方法有synchronized关键字修饰,看似线程安全的,但是方法中调用了,System.out.println();而out的锁是PrintStream的实例,和num方法不是一个锁,所以多个线程可以同时访问,这就出现了线程安全问题。你的赋值是安全的,但是out输出lt_num1时就不安全了。
有可能在out时lt_num1的值刚好被你的其它线程修改了,所以输出结果大部分重复的。是System.out.println();影响了输出结果。不是你的代码自身,而是调用System.out.println();失去了线程安全。

你这是误人子弟,同步块肯定没有线程安全问题
问题在于调用num(lt_num2);lt_num2 copy引用时(调用方法都会将参数复制一份引用/值传递到调用方法)主线程不断修改lt_num2的指向,如果多个子线程在下个修改之前成功复制了引用就会导致打印同样的值,你可以写代码测试一把
qq_40674493 2019-05-15
  • 打赏
  • 举报
回复
run方法中调用num方法时,有可能主线程已经再次修改了lt_num2值。改成局部变量后,每次都创建新的变量,其他线程无法改变它的值,这样run方法中调用num方法时肯定是最初给他的值。
qq_40674493 2019-05-15
  • 打赏
  • 举报
回复
主线程和其他线程不同步
qq_39936465 2019-05-15
  • 打赏
  • 举报
回复
引用 2 楼 隐语者 的回复:
您好,我试了你的方法,确实可行,现在产生了两种疑问:

1、我的程序业务逻辑,那个“lt_num2”必须使用静态变量,因为会有别的类会对这个参数进行共享,为什么使用静态参数进行赋值会产生重复数据,使用局部变量进行赋值就不会产生重复?

2、你看你在局部变量“lt_num2”使用了final,我觉得可以去掉final(限制变量不可重赋值)也是可行的,是否有其他情况会产生让必须使用final修饰这个局部变量

请解析下,初学者不甚感激。



因为你的线程都是共用同一个变量,线程为乱序执行你不能保证你的变量值和你的线程相匹配。 用了final后保证了该轮线程的变量不被其他线程的变量修改。
qq_40674493 2019-05-15
  • 打赏
  • 举报
回复
num这个方法有synchronized关键字修饰,看似线程安全的,但是方法中调用了,System.out.println();而out的锁是PrintStream的实例,和num方法不是一个锁,所以多个线程可以同时访问,这就出现了线程安全问题。你的赋值是安全的,但是out输出lt_num1时就不安全了。
有可能在out时lt_num1的值刚好被你的其它线程修改了,所以输出结果大部分重复的。是System.out.println();影响了输出结果。不是你的代码自身,而是调用System.out.println();失去了线程安全。
maradona1984 2019-05-15
  • 打赏
  • 举报
回复
引用 2 楼 隐语者 的回复:
[quote=引用 1 楼 maradona1984 的回复:]
lt_num1这个变量不需要
lt_num2定义到for循环里面
也就是,你对多线程的理解很模糊

public static void main(String[] args) throws Exception {
ExecutorService Service=Executors.newFixedThreadPool(20);
long begin_time,end_time;
begin_time=System.currentTimeMillis();
for(int i=1;i<=20;i++) {
final Integer lt_num2=i;
Service.submit(new Thread(new Runnable() {
@Override
public void run() {
num(lt_num2);
}
}));
}
end_time=System.currentTimeMillis();
System.out.println("Occupation time-->"+(end_time-begin_time));
}

private synchronized static void num(int lt_1){
System.err.println(lt_1);
}



您好,我试了你的方法,确实可行,现在产生了两种疑问:

1、我的程序业务逻辑,那个“lt_num2”必须使用静态变量,因为会有别的类会对这个参数进行共享,为什么使用静态参数进行赋值会产生重复数据,使用局部变量进行赋值就不会产生重复?

2、你看你在局部变量“lt_num2”使用了final,我觉得可以去掉final(限制变量不可重赋值)也是可行的,是否有其他情况会产生让必须使用final修饰这个局部变量

请解析下,初学者不甚感激。

[/quote]
1.这也是为啥我说你对多线程很模糊的原因,这个你还是自己思考的好,说不清,你脑子里必须有对代码执行的顺序有清晰的认识,我只能提醒你,同一个线程里的代码是顺序执行的,不同线程里的执行是乱序的(这句话可能会让你产生错误的理解,懒得解释),而且你原来的代码违反常规套路,懒得去想,简单的东西被你搞得复杂,想着累
2.你把final删掉能编译通过?我也不知道你用的什么jdk版本,7和7以下匿名类访问局部变量应该必须加final修饰的,jdk8做了优化罢了,但实际上编译时加了final修饰的

隐语者 2019-05-15
  • 打赏
  • 举报
回复
引用 1 楼 maradona1984 的回复:
lt_num1这个变量不需要 lt_num2定义到for循环里面 也就是,你对多线程的理解很模糊

      public static void main(String[] args) throws Exception {
    	  ExecutorService Service=Executors.newFixedThreadPool(20);
    	  long begin_time,end_time;
    	  begin_time=System.currentTimeMillis();
    	  for(int i=1;i<=20;i++) {
    		 final Integer lt_num2=i;
    		  Service.submit(new Thread(new Runnable() {
    				  @Override
    				  public void run() {
						num(lt_num2);
    				  }
    			}));
    	  }
    	  end_time=System.currentTimeMillis();
    	  System.out.println("Occupation time-->"+(end_time-begin_time));
      }

      private synchronized static void num(int lt_1){
    	  System.err.println(lt_1);
      }

您好,我试了你的方法,确实可行,现在产生了两种疑问: 1、我的程序业务逻辑,那个“lt_num2”必须使用静态变量,因为会有别的类会对这个参数进行共享,为什么使用静态参数进行赋值会产生重复数据,使用局部变量进行赋值就不会产生重复? 2、你看你在局部变量“lt_num2”使用了final,我觉得可以去掉final(限制变量不可重赋值)也是可行的,是否有其他情况会产生让必须使用final修饰这个局部变量 请解析下,初学者不甚感激。
maradona1984 2019-05-15
  • 打赏
  • 举报
回复
lt_num1这个变量不需要
lt_num2定义到for循环里面
也就是,你对多线程的理解很模糊

public static void main(String[] args) throws Exception {
ExecutorService Service=Executors.newFixedThreadPool(20);
long begin_time,end_time;
begin_time=System.currentTimeMillis();
for(int i=1;i<=20;i++) {
final Integer lt_num2=i;
Service.submit(new Thread(new Runnable() {
@Override
public void run() {
num(lt_num2);
}
}));
}
end_time=System.currentTimeMillis();
System.out.println("Occupation time-->"+(end_time-begin_time));
}

private synchronized static void num(int lt_1){
System.err.println(lt_1);
}

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧