java实现线程共享通过2种方式:静态变量、实例化变量
【理解概念】
静态变量:静态变量即类变量,位于方法区,为所有该类下的对象共享,共享一份内存,一旦静态变量被修改,其他对象均对修改可见,故线程非安全。
实例变量: 实例变量为对象实例私有,在虚拟机的堆中分配,若在系统中只存在一个此对象的实例,在多线程环境下,“犹如”静态变量那样,被某个线程修改后,其他线程对修改均可见,故线程非安全;如果每个线程执行都是在不同的对象中,那对象与对象之间的实例变量的修改将互不影响,故线程安全。
【2者的区别】
存放位置
- 类变量随着类的加载而存在于方法区中。
- 实例变量随着对象的建立而存在于堆内存中。
生命周期
- 类变量生命周期最长,随着类的消失而消失。
- 实例变量生命周期随着对象的消失而消失。
类变量和实例变量的区别在于:类变量是所有对象共有,其中一个对象将它值改变,其他对象得到的就是改变后的结果;而实例变量则属对象私有,某一个对象将其值改变,不影响其他对象。
例子:
public class BB {
public static int as = 0; // 静态变量
public int ac = 0; // 实例变量
}
public class TT {
public static void main(String[] args) throws Exception{
BB bb1 = new BB();
BB bb2 = new BB();
// 设置 bb1 的静态变量为 3
bb1.as = 3;
// 设置 bb1 的实例变量为 4
bb1.ac = 4;
System.out.println("bb2获取的as值为:"+bb2.as);
System.out.println("bb2获取的ac值为:"+bb2.ac);
}
}
bb2获取的as值为:3
bb2获取的ac值为:0
- 静态变量是针对所有对象的,所以bb1改变as, bb2的as也改变。
- 实例只改变自身的,所以bb1对象的ac改变,不影响对象bb2的ac变量。
测试例子,卖票,注意都是线程不安全的
【静态变量测试】static int ticket = 10;
public class TestThread extends Thread {
static int ticket = 10;
@Override
public void run() {
while (ticket > 0){
System.out.println(Thread.currentThread().getName() + " 卖票:"+ticket);
ticket -- ;
}
}
}
public static void main(String[] args) throws Exception{
for (int i = 0; i < 2; i++) {
TestThread testThread = new TestThread();
testThread.start();
}
}
Thread-1 卖票:10
Thread-0 卖票:10
Thread-0 卖票:8
Thread-0 卖票:7
Thread-0 卖票:6
Thread-0 卖票:5
Thread-0 卖票:4
Thread-0 卖票:3
Thread-0 卖票:2
Thread-1 卖票:9
Thread-0 卖票:1
【实例化变量】
public class TestThread implements Runnable {
int ticket = 10;
@Override
public void run() {
while (ticket > 0){
System.out.println(Thread.currentThread().getName() + " 卖票:"+ticket);
ticket -- ;
}
}
}
public static void main(String[] args) throws Exception{
TestThread testThread = new TestThread();
for (int i = 0; i < 2; i++) {
new Thread(testThread).start();
}
}
Thread-0 卖票:10
Thread-0 卖票:9
Thread-0 卖票:8
Thread-0 卖票:7
Thread-0 卖票:6
Thread-0 卖票:5
Thread-0 卖票:4
Thread-0 卖票:3
Thread-0 卖票:2
Thread-0 卖票:1
Thread-1 卖票:10
如上代码中,虽然TestThread类中的ticket是普通成员变量,但同一个TestThread实例可以在多个线程对象间共享,如上main函数中,虽然创建了2个线程对象,但它们的target都是同一个TestThread对象,所以2个线程操作的ticket都是同一个实例的变量,从而达到信息共享的效果。