多线程基础三、实例变量和线程安全

自定义线程类中的实例变量针对其他线程可以有共享和不共享之分,这在多个线程之间进行交互时是很重要的一个技术点。

1. 不共享数据的情况:

public class MyThread extends Thread {
    private int count = 5;
    public MyThread (String name) {
        super();
        this.setName(name); //设置线程名称
    }

    @Override
    public void run() {
        super.run();
        while (count > 0) {
            count--;
            System.out.println("由 " + this.currentThread().getName() + " 计算,count = " + count);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        MyThread a = new MyThread("AAA");
        MyThread b = new MyThread("BBB");
        MyThread c = new MyThread("CCC");
        a.start();
        b.start();
        c.start();
        /*
        运行结果:三个线程各自操作自己的count变量
        由 CCC 计算,count = 4
        由 CCC 计算,count = 3
        由 CCC 计算,count = 2
        由 CCC 计算,count = 1
        由 CCC 计算,count = 0
        由 BBB 计算,count = 4
        由 BBB 计算,count = 3
        由 BBB 计算,count = 2
        由 BBB 计算,count = 1
        由 BBB 计算,count = 0
        由 AAA 计算,count = 4
        由 AAA 计算,count = 3
        由 AAA 计算,count = 2
        由 AAA 计算,count = 1
        由 AAA 计算,count = 0
         */
    }
}
2. 共享数据的情况:
public class MyThread2 extends Thread {
    private int count = 5;

    @Override
    public void run() {
        super.run();
        //不使用 while 循环,因为使用后其他线程得不到运行的机会,一直由一个线程进行
        count--;
        System.out.println("由 " + this.currentThread().getName() + " 计算,count = " + count);
    }
}
public class Test2 {
    public static void main(String[] args) {
        MyThread2 mt = new MyThread2();
        Thread a = new Thread(mt,"AAA");
        Thread b = new Thread(mt,"BBB");
        Thread c = new Thread(mt,"CCC");
        Thread d = new Thread(mt,"DDD");
        Thread e = new Thread(mt,"EEE");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
        /*
        运行结果:三个线程同时对 count 进行处理,产生非线程安全问题
        由 AAA 计算,count = 2
        由 DDD 计算,count = 1
        由 BBB 计算,count = 2
        由 EEE 计算,count = 2
        由 CCC 计算,count = 0
         */
    }
}


那么如何避免上述的非线程安全问题? 

非线程安全主要是指多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程。

使用 synchronize 关键字

public class ThreadSafe extends Thread {
    private int count = 5;

    @Override
    public synchronized void run() {
        super.run();
        //不使用 while 循环,因为使用后其他线程得不到运行的机会,一直由一个线程进行
        count--;
        System.out.println("由 " + this.currentThread().getName() + " 计算,count = " + count);
    }
}
在 run 方法前加上 synchronized 关键字,当一个线程调用 run 前,先判断 run 方法有没有被上锁,如果上锁说明有其他线程正在调用 run 方法,必须等其他线程对 run 方法调用结束后才可以执行 run 方法。这个线程会不断地尝试拿这把锁,知道能够拿到为止,那么就会出现多个线程同时去争这把锁synchronized 可以再任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。

3. i-- 与 System.out.printlin() 的异常

上面说到解决非线程安全问题使用的是 synchronized 关键字,那么我们看下面这个例子:

public class PrintlnThread extends Thread {
    private int i = 5;
    @Override
    public void run() {
        System.out.println("i = " + (i--) + " threadName = " + Thread.currentThread().getName());
    }
}
public class TestPrintlnThread {
    public static void main(String[] args) {
        PrintlnThread pt = new PrintlnThread();
        Thread t1 = new Thread(pt);
        Thread t2 = new Thread(pt);
        Thread t3 = new Thread(pt);
        Thread t4 = new Thread(pt);
        Thread t5 = new Thread(pt);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        /*
        运行结果:
        i = 5 threadName = Thread-2
        i = 1 threadName = Thread-1
        i = 2 threadName = Thread-4
        i = 4 threadName = Thread-5
        i = 3 threadName = Thread-3
         */
    }
}
虽然 println() 方法在内部是同步的,但是 i-- 的操作是在进入 printlin() 之前发生的,所以有非线程安全问题的概率。为避免这个问题,还是需要使用同步方法。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值