Java多线程(3) 线程的同步-上
2017-12-13 00:09
441 查看
概述
最近比较忙,线程的同步其实在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才可以释放锁。
相关文章推荐
- Java笔记3 多线程<1>线程概述、多线程的创建、多线程的安全问题、静态同步函数的锁、死锁
- java多线程:线程的同步与锁
- JAVA多线程--线程的同步安全
- 深入java 多线程线程间的同步
- java多线程学习总结之四:线程的同步
- java多线程之线程的同步与锁定(转)
- Java多线程-线程的同步与锁
- 多线程系列二——java线程间的互斥与同步
- Java多线程-线程的同步(同步代码块)
- java多线程之线程同步七种方式代码示例
- 黑马程序员-18-java基础-多线程(1)-线程与同步
- Java多线程(5) 线程的同步-下
- Java多线程编程总结笔记——六线程的同步与锁
- Java多线程-线程的同步与锁
- Java多线程-线程的同步与锁
- Java多线程线程、同步代码块、同步函数、死锁
- Java多线程-线程的同步与锁
- JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制
- [Java]Java多线程数据安全(同步线程的方法)
- Java 多线程详解(三)------线程的同步