Java多线程(3) 线程的同步-上

概述

最近比较忙,线程的同步其实在12月上旬就完成的差不多了,但是一直因为项目的关系,所以没有办法及时的发出来,在新的一年里会努力的扩充自己的技术栈。在多线程的实际应用中,多个线程经常需要共享对同一数据的存取,这样的情况就称为竞争条件。假设i的值为0,A线程执行i=i+1,B线程执行i=i+2,那么最后的值有可能不是3,而是1或者2。因为A读取i后尚未完成计算写入,B线程马上读取i,此时i的值还是0,当A线程执行后i的值变为1,但是B线程已经不会再重新读取i了,在B线程完成计算写入i的值就是2了,当然,如果A线程后完成计算,值也有可能就是1了。所以,多线程中同步是一个很重要的问题(我更喜欢叫控制线程)。

没有同步存取的线程

先看一个没有进行同步存取的例子,在这个例子当中共有2个子线程,1个负责累加,对value从加1至加1000,1个负责累减,对value从减1000至减1,那么接下来就开始贴代码。

package com.utopia.controls;

public class NoControl {
    private static int value;

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        // 对value赋初值
        value = 0;
        System.out.println(value);
        // 累加线程
        Thread threadAdd = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                for (int i = 1; i <= 1000; i++) {
                    value += i;
                }
            }
        });
        // 累减线程
        Thread threadSub = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                for (int i = 1000; i > 0; i--) {
                    value -= i;
                }
            }
        });
        // 同时运行俩个线程
        threadAdd.start();
        threadSub.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(value);
    }

}

从理论上来讲,最后输出的结果应该是0,但是在实际运行中,虽然有时候结果也是0,但是大部分结果并不是0,如果把10000调至100000,那么结果出现不是0的概率将会更大。
其中一个结果截图:
这里写图片描述
如果使用多线程,那么出现这种情况我们必然是无法容忍的,所以就有了锁对象。

锁对象

其实,锁对象的概念是很好理解的。在上面我们分析过,出现存取不同步的原因是,Java语言中一条代码:i+=1;在翻译成机器码运行时会变成:读取i;完成加法;写到i的内存中;一共三条指令(在实际中,可能要比三条还多),A线程读取i进行计算加法时,B线程可能将减法的计算结果保存到i中,但是A线程再次保存就会将B线程的值进行覆盖,举个例子:
value = 0;
A: value += 10;
B: value -= 10;
1.A 读取value是0
2.B 读取value是0
3.B 计算得数是 -10 A计算得数是10
4.B 保存value是-10
5.A保存value是10
虽然理论上value的值是0,但最后结果value就是10
如果,我们可以加个锁,在A线程对value执行代码时,不允许B线程去执行,那么就不会出现这种情况了。对锁的使用一般分为3步:
1.对锁进行实例化
2.对线程进行加锁
3.对线程进行解锁
代码如图所示:

package com.utopia.controls;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Control {
    private static int value;
    // 对锁进行实例化
    private static Lock valueLock = new ReentrantLock();

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        // 对value赋初值
        value = 0;
        System.out.println(value);
        // 累加线程
        Thread threadAdd = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                for (int i = 1; i <= 10000; i++) {
                    // 对累加线程上锁
                    valueLock.lock();
                    value += i;
                    // 对累加线程解锁
                    valueLock.unlock();
                }
            }
        });
        // 累减线程
        Thread threadSub = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                for (int i = 10000; i > 0; i--) {
                    // 对累减线程上锁
                    valueLock.lock();
                    value -= i;
                    // 对累减线程去锁
                    valueLock.unlock();
                }
            }
        });
        // 同时运行俩个线程
        threadAdd.start();
        threadSub.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(value);
    }

}

在进行线程同步后,可以发现结果永远都是0,如图:
这里写图片描述
锁是可重入的,线程可以重复的获得已经持有的锁,锁保持一个持有计数来跟踪lock方法的嵌套调用。例如:线程A在调用lock后持有锁,线程B在线程A调用unlock释放之前如果调用lock会发生阻塞,但是如果线程A再次调用lock方法则不会发生阻塞,但是线程A必须要执行2次unlock才可以释放锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值