一、线程之间的数据共享—线程通信
大部分场景下,几个线程之间是需要协调配合工作(线程之间需要进行数据交换),一起完成一个总目标的。由于我们的线程都属于同一个进程,所以共享有OS分配过来的同样的资源(其中优先关注内存资源)
1.JVM下的内存区域划分PC保存区(PC);栈区(本地方法栈;虚拟机栈);堆;方法区;运行时常量池
这些内存资源是属于进程的。理论上来讲,确实也是这个进程下所有线程的,但在进程内部也有分配机制,这些内存在线程中还会分配一次。(类比:家庭财务,属于家庭,但细分每个人都有对应花销)并且不是操作系统做分配了,是进程内部分配。
堆、方法区、运行时常量池是整个进程(JVM)只有一份,PC(保存PC值)、栈(虚拟机栈、本地方法栈)是每个线程独一份(各自有各自的)【对象是共有的,加载的类是共有的】
1)为什么每个线程要有自己的PC
每个线程都是独立的执行流,要下一条要执行的指令和其他线程无关,所以有自己的PC。
2)为什么每个线程都得有自己的栈每个线程都是独立的执行流,各自有各自调用的方法链,有各自要处理的临时数据,所以栈也是独一份的。
3)体现到代码中的共有和私有
局部变量,保存在栈桢中,也就是保存在栈中,是线程私有
类对象(Class对象-关于类的对象)、静态属性,保存在方法区中,所以是线程之间共享的【前提:有访问权限】
对象(对象内部属性),保存在堆中,所以是线程之间是可以共享的【前提:线程持有该对象的引用】(见4)5)这个两个例子)
1)区分不同内存块的r
public class Main {
static class MyThread extends Thread{
@Override
public void run() {
int r=0;//r存在于子线程run方法的栈帧中
for (int i=0;i<1000;i++){
r++;
}
System.out.println(Thread.currentThread().getName()+":"+r);
}
}
public static void main(String[] args) throws InterruptedException {
int r=0;//r存在于主线程main的栈帧中
MyThread t=new MyThread();
t.start();//开启线程
t.join();//等待线程结束
System.out.println(r);//此处r仍为0,因为++的是子线程的r,不是主线程的
}
}
2)一共在内存中开辟了几个名叫r的内存块
public class Main {
static class MyThread extends Thread{
private final int total;
public MyThread(int total){
this.total=total;
}
@Override
public void run() {
int r=0;//r存在于子线程run方法的栈帧中
for (int i=0;i<1000;i++){
r++;
}
System.out.println(Thread.currentThread().getName()+":"+r);
}
}
public static void main(String[] args) throws InterruptedException {
int r=0;//r存在于主线程main的栈帧中
MyThread t1=new MyThread(1000);
t1.start();//开启线程
MyThread t2=new MyThread(1000);
t2.start();//开启线程
MyThread t3=new MyThread(1000);
t3.start();//开启线程
t1.join();//
t2.join();//等待线程结束
t3.join();//等待线程结束
System.out.println(r);//此处r仍为0,因为++的是子线程调用到的r,不是主线程的
}
}
结果:
Thread-0:1000
Thread-2:1000
Thread-1:1000
0
3)r为静态属性时,整个进程中就这一个r
public class Main {
static int r=0;//定义为静态属性
//进程中就这一个r
static class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
r++;
}
System.out.println(getName()+":"+r);
}
}
public static void main(String[] args) throws InterruptedException {
MyThread t=new MyThread();
t.start();
t.join();
System.out.println(Thread.currentThread().getName()+":"+r);
}
}
结果:
Thread-0:1000
main:1000
分析:主线程虽然不对r做更改,但是子线程改变了r的值,主线程也就同步更改了,这也就是数据共享。
4)对象没有共享的情况
public class Main {
static class SomeObject{
int r=0;
}
static class MyThread extends Thread{
@Override
public void run() {
SomeObject someObject=new SomeObject();
for (int i = 0; i < 1000; i++) {
someObject.r++;
}
System.out.println(getName()+":"+someObject.r);
}
}
public static void main(String[] args) throws InterruptedException {
SomeObject someObject=new SomeObject();
MyThread t=new MyThread();
t.start();
t.join();
System.out.println(Thread.currentThread().getName()+":"+someObject.r);
}
}
//结果:
//Thread-0:1000
//main:0
5)对象之间共享的情况-操作的是同一批数据
package com.wy.about_data_share.demo5;
//想办法让子线程和主线程的引用指向同一个对象-利用构造方法
public class Main {
static class SomeObject{
int r=0;
}
static class MyThread extends Thread{
private final SomeObject someObject;
public MyThread(SomeObject someObject){
this.someObject=someObject;//这个someObject指向主线程main方法栈的someObject指向的对象
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
someObject.r++;
}
System.out.println(getName()+":"+someObject.r);
}
}
public static void main(String[] args) throws InterruptedException {
SomeObject someObject=new SomeObject();//只有这个地方创建对象
MyThread t=new MyThread(someObject);//把SomeObject对象的引用传入
t.start();
t.join();
System.out.println(Thread.currentThread().getName()+":"+someObject.r);
}
}
结果:
Thread-0:1000
main:1000



