多线程信息共享(1)
线程类:
---- 通过继承Thread或实现Runnable
---- 通过start方法,调用run方法,run方法工作
---- 线程run结束之后,线程退出
粗粒度:子线程与子线程之间,子线程和main线程之间缺乏交流(之前的多线程就是一中粗粒度的执行模式)
细粒度:线程之间有信息交流通讯(细粒度的编程模式)
对于细粒度的执行模式,需要注意:
(1)可以通过共享变量达到信息共享
(2)JDK原生库暂时不支持点对点发送消息(类似MPI并行库直接发送消息)
MPI并行库是一个C / C++的并行库,可以支持像 线程0线程1发送一封消息,或者说线程0向所有的线程群发一条消息,像这种点对点的消息,Java还不支持
多线程信息共享(2):
----通过共享变量在多个线程中共享信息
(1)static变量(是这个类所有的对象,都共享的一个变量)
(2)同一个Runnable类的成员变量
这样的共享变量在多个线程中实际上就是一个拷贝对象,理解成下图,它们要用数据,直接从内存里面拿,拿到在自己的线程里去运算
注意:如果一个线程类继承了Thread类,那么它的信息共享只能通过static来,而不能通过普通的成员变量(线程类中的成员变量),普通的成员变量达不到信息共享的目的
多线程信息共享(3):
---- 多线程信息共享存在的两个问题:
(1)工作缓存副本:每个线程都有自己的工作缓存,这个线程需要数据的时候,他是从内存里面加载完数据,放到工作缓存里面来。现在有一个线程把工作缓存里面的值给修改了,这里修改好的数据其他线程没有看到,他们还是用自己的工作缓存里面的数据,进行运算,这就导致每个线程的工作缓存里面并没有反映出最新的变量是多少,大家用的都是前一刻的变量的值,而不是最新的值,所以导致数据不一致的问题
(2)关键步骤缺乏加锁限制:关键步骤,即比如对重要变量做操作如买火车票问题中的票数。加锁限制,简单地说就是这样的一个关键变量,当一个线程去修改它的时候,要限制一个只能有一个线程去修改它,不能多个人同时修改它,否则修改会引起错乱
下图是Java多线程内存模型(中间是JVM控制)
---- 原子性操作:只有一个操作,原子已经不可再分了。比如i++就不是原子性操作,(在Java的内存模型里面)分成几个步骤执行的:
(1)读取主存RAM中的i(正本,正式的值)到工作缓存(副本,复制品)中去
(2)每个CPU执行(副本)i+1操作
(3)CPU将结果写入到缓存(副本)中
(4)数据从公座缓存(副本)刷新到主存(正本)中
多线程信息共享(4):
---- 变量副本问题的解决方法:
(1)采用 volatile 关键字修饰变量,保证不同线程对共享变量操作时都是可见的:我们在一个线程的工作缓存中修改了一个变量的值,结果其他线程的工作缓存中的值没有刷新。volatile关键字可以帮助我们,一旦一个变量在工作缓存区里面被修改了,其他的线程也马上也能看到
(2)关键步骤加锁限制
---- 互斥:某一个线程运行一个代码段(关键区),其他线程不能同时运行这个代码段
---- 多个线程的运行,必须按照某一种规定的先后顺序来运行
---- 互斥是同步的一种特例,是同步里面最简单的一种方式
---- 互斥的关键字是synchronized:
synchronized可以修饰代码块 / 函数,只能一个线程进入
synchronized加大性能负担,但是使用简便