启动多线程有两种方式:(都是在主线程main线程下)

1. 使用同一个线程对象来启多个线程

2. 使用多个线程对象来启多个线程

这两种方式有什么区别呢?先贴上代码举例说明:

这是使用线程对象MyRunnable的同一个实例r来启动了两个线程

MyRunnable r = new MyRunnable();
Thread ta = new Thread(r,"Thread-A");
Thread tb = new Thread(r,"Thread-B");
ta.start();
tb.start();     

这是使用线程对象MyRunnable的两个不同的实例r来启动了两个线程

MyRunnable r1 = new MyRunnable();
MyRunnable r2 = new MyRunnable();Thread ta = new Thread(r1,"Thread-A");
Thread tb = new Thread(r2,"Thread-B"); ta.start();
tb.start();

那么使用这两种方式的区别在哪里呢?我们紧接着看下面的代码的运行结果:

public class MyRunnable implements Runnable {private Foo foo =new Foo(); public static void main(String[] args) {MyRunnable r = new MyRunnable();Thread ta = new Thread(r,"Thread-A"); Thread tb = new Thread(r,"Thread-B"); ta.start(); tb.start(); /*MyRunnable r1 = new MyRunnable();MyRunnable r2 = new MyRunnable();Thread ta = new Thread(r1,"Thread-A"); Thread tb = new Thread(r2,"Thread-B"); ta.start(); tb.start();*/} public void run() {for (int i = 0; i < 3; i++) {this.fix(30);try {Thread.sleep(1); } catch (InterruptedException e) {e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " :当前foo对象的x值= " + foo.getX());} } public int fix(int y) {return foo.fix(y);}
}class Foo {private int x = 100;public int getX() {return x;} public int fix(int y) {x = x - y; return x;}
}

使用同一个线程对象启多个线程的运行结果:

Thread-B :当前foo对象的x值= 40
Thread-B :当前foo对象的x值= 10
Thread-A :当前foo对象的x值= -20
Thread-B :当前foo对象的x值= -50
Thread-A :当前foo对象的x值= -50
Thread-A :当前foo对象的x值= -80

使用多个线程对象启动多个线程的运行结果:

Thread-A :当前foo对象的x值= 70
Thread-B :当前foo对象的x值= 70
Thread-B :当前foo对象的x值= 40
Thread-A :当前foo对象的x值= 40
Thread-A :当前foo对象的x值= 10
Thread-B :当前foo对象的x值= 10

我们可以看到在改变值的过程中,值串了。并且线程执行也是串的,两个线程之间在相互争抢执行。
(线程ta的run方法还没有执行完,tb的run方法争抢到了cpu资源从而执行)

在改变值的过程中,值改变是对的。线程执行是串的。(值没有串,是因为foo是私有变量,属于ta,tb所各自私有)

是我们不能允许的,因为值串了。
②都出现的线程之间相互争抢的问题,就看我们的业务实现了。
使用多线程时,我们有时就是想启用多个线程同时去干不同的事情,这时它们相互争抢执行就是我们想要的。
有时,在多个线程同时访问一个方法时,我们希望当一个线程执行完这个方法后,再让其他的线程去执行,这时,我们就要避免线程之间相互争抢的问题,也就是使用同步锁机制来控制。

好,如果我们现在想要run()方法执行完了之后,其他线程才能再次进入run()方法来执行。我们用同步关键字synchronized来实现。如下:

同步方法:

synchronized public void run() {for (int i = 0; i < 3; i++) {this.fix(30);try {Thread.sleep(1); } catch (InterruptedException e) {e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " :当前foo对象的x值= " + foo.getX());} } 

同步块:

public void run() {synchronized(this){for (int i = 0; i < 3; i++) {this.fix(30);try {Thread.sleep(1); } catch (InterruptedException e) {e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " :当前foo对象的x值= " + foo.getX());} }} 

对于我们使用上面的同步方法和同步块都能得到如下的输出:

Thread-A :当前foo对象的x值= 70
Thread-A :当前foo对象的x值= 40
Thread-A :当前foo对象的x值= 10
Thread-B :当前foo对象的x值= -20
Thread-B :当前foo对象的x值= -50
Thread-B :当前foo对象的x值= -80

对于我们使用上面的同步方法和同步块却得到如下的输出:

Thread-A :当前foo对象的x值= 70
Thread-B :当前foo对象的x值= 70
Thread-A :当前foo对象的x值= 40
Thread-B :当前foo对象的x值= 40
Thread-A :当前foo对象的x值= 10
Thread-B :当前foo对象的x值= 10

的结果与我们预期的是一样的,但是却不如我们的预期,各个线程之间还是在相互争抢执行。

为什么呢?我们不是都已经使用synchronized同步了吗?

导致这个问题的根源就是对象锁的问题。

中使用同步方法时,线程ta,tb对应的对象锁都为MyRunnable的实例对象r,对象锁共享且唯一,所以起到了同步的作用。
同理,使用同步块时,ta,tb的对象锁也都是MyRunnable的实例对象r,故也能达到效果。

但对于不同的是,使用方法同步和块同步时,线程ta,tb对应的对象锁分别是各自的线程对象的实例,即ta-->r1,tb-->r2。故线程ta,tb分别持有各自的对象锁,所以达不到同步的效果。

如果换成如下代码执行

public void run() {synchronized("123"){for (int i = 0; i < 3; i++) {this.fix(30);try {Thread.sleep(1); } catch (InterruptedException e) {e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " :当前foo对象的x值= " + foo.getX());} }} 

我们得到如下结果:

Thread-A :当前foo对象的x值= 70
Thread-A :当前foo对象的x值= 40
Thread-A :当前foo对象的x值= 10
Thread-B :当前foo对象的x值= 70
Thread-B :当前foo对象的x值= 40
Thread-B :当前foo对象的x值= 10

这下就和我们的预期一样了。ta,tb线程都持有字符串"123"作为对象锁,ta,tb线程中的"123"都指向相同的内存地址,故对象锁相同且共享,故能达到同步效果。(为什么ta,tb中的"123"指向相同的内存地址,与String对象本身比较特殊有关,在此不赘述)

对于文章中的对象锁问题有疑问的,可以参见另一篇博文:http://www.cnblogs.com/kevin-yuan/archive/2013/04/27/3047511.html

转载于:https://www.cnblogs.com/kevin-yuan/p/4111242.html

启动多线程的两种情况比较相关推荐

  1. 数据库启动报错两种情况的解决办法

    通常服务器在突然断电或者频繁关机的情况下很容易造成数据库损坏,然后启动不了数据库,针对这种情况出现最多的有两种, 其一:启动的时候数据文件损坏 这种情况比较简单, 1.  首先关闭数据库 sql> ...

  2. 【Java_多线程并发编程】基础篇—线程状态及实现多线程的两种方式

    1.Java多线程的概念 同一时间段内,位于同一处理器上多个已开启但未执行完毕的线程叫做多线程.他们通过轮寻获得CPU处理时间,从而在宏观上构成一种同时在执行的假象,实质上在任意时刻只有一个线程获得C ...

  3. 多线程基础-实现多线程的两种方式(二)

    实现多线程的两种方式: 1.实现Runnable public interface Runnable {public abstract void run(); }// RunnableTest.jav ...

  4. 修改db2管理服务器,创建DB2管理服务器的两种情况

    DB2管理服务器在创建时分为创建一个和创建多个两种情况,下面就为您详细介绍这两种创建DB2管理服务器的情况,供您参考学习. 一.创建DB2管理服务器(只能创建一个) 1.首先创建管理服务组用户(可不建 ...

  5. 通过root用户与hadoop用户分别启动zookeeper,两种启动结果为什么会截然不同?

    虚拟机1: 虚拟机1是在hadoop下启动zookeeper,然后在hadoop查看,zookeeper已启动,但是换到root下查看却无zookeeper的信息. 虚拟机2: 虚拟机2是在root下 ...

  6. java找不到java.dll(两种情况)

    第一种情况: Windows键+R打开运行框输入cmd 输入Java后找不到Java.dll 直接附图 这种情况所遇到的问题分两种情况: 1.由于之前安装的jdk未卸载干净,重新装了新的版本,版本之间 ...

  7. java多线程同步的四种方法_java中实现多线程的两种方法

    java多线程有几种实现方法,都是什么?同步有几种实java中多线程的实现方法有两种:1.直接继承thread类:2.实现runnable接口:同步的实现方法有五种:1.同步方法:2.同步代码块:3. ...

  8. win10支持8t 硬盘_WD 西数 My Book 10T 8T拆盘后不识别的两种情况及解决

    WD 西数 My Book 10T 8T拆盘后不识别的两种情况及解决 2019-05-12 03:49:19 25点赞 247收藏 38评论 受大妈这个贴子的蛊惑:国内中亚Prime会员.绝对值:WD ...

  9. 进一步封装axios并调用其读取数据(吐槽~在安卓9.0以下或者IOS10.X以下手机端H5页面不支持,在这两种情况下的系统只能使用ajax或者原生js请求后台数据)

    注意!!!(修改于2020年7月18日) 在安卓9.0以下或者IOS10.X以下手机端H5页面不支持,在这两种情况下的系统只能使用ajax或者原生js请求后台数据 报错截图如下 报错内容: {&quo ...

最新文章

  1. 订单管理之获取订单表表列表数据
  2. 如何用windows系统访问Linux系统的web网站
  3. Python基础——for/while循环
  4. 78折用计算机怎么算,一分钟速算口诀,计算速度秒杀计算器
  5. 分享一个有趣的网站“让我帮你百度一下“
  6. 解决Hadoop总是处于安全模式的问题
  7. mysql sysdate本周_mysql 查询当天、本周,本月,上一个月的数据
  8. 【Java从0到架构师】JDBC、Spring JDBC、JUnit
  9. 一个资源管理系统的设计--基于cgroup机制
  10. .sig 签名文件怎么使用
  11. IPD流程在华为15年发展历程…
  12. Mac Os 安装配置Maven以及IntelliJ IDEA Maven使用
  13. kd树 python实现_Python语言描述KNN算法与Kd树
  14. 建筑竞赛获奖项目解析国外教程
  15. linux系统 插上硬盘认不到,关于Linux系统增加SCSI硬盘不识别的问题及解决办法
  16. 上海小伙三次成功创业,资产达上十亿被称为“创业神童”
  17. 索尼为收购工作室准备,对抗微软?
  18. c++进制转换(完整)
  19. td强制换行《http://www.cnblogs.com/Fooo/archive/2011/03/28/1998048.html》
  20. 自学 java如何入门?

热门文章

  1. 谷歌搜索和谷歌站内搜索
  2. android APP常用的颜色及代码
  3. ubuntu下vim + ctags + taglist配置和使
  4. 台式计算机无线网卡,台式机用无线网卡,详细教您台式机怎么用无线网卡
  5. ar路由器 pppoe下发ipv6 dns_移动宽带如何获取IPV6地址
  6. 中油即时通信电脑版_联想超智能电脑横空出世 电脑适应人已成现实
  7. JavaScript中几个优雅的运算符使用技巧
  8. linux rmi端口,RMI 两个端口 - 差不多先生的个人空间 - OSCHINA - 中文开源技术交流社区...
  9. 2021阿里巴巴大数据技术公开课第一季:外部工具连接SaaS模式云数仓MaxCompute实战
  10. Flagger on ASM——基于Mixerless Telemetry实现渐进式灰度发布系列 1 遥测数据