一、同步问题提出

线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。
例如:两个线程ThreadA、ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据。

package cn.thread;public class Foo {private int x = 100;public int getX() {return x;}public int fix(int y) {x = x - y;return x;}
}

package cn.thread;public class MyRunnable implements Runnable {private Foo foo = new Foo();public static void main(String[] args) {MyRunnable run = new MyRunnable();Thread ta = new Thread(run, "Thread-A");Thread tb = new Thread(run, "Thread-B");ta.start();tb.start();}public void run() {for (int i = 0; i < 3; i++) {this.fix(30);try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " : 当前foo对象的x值= " + foo.getX());}}public int fix(int y) {return foo.fix(y);}}

Thread-B : 当前foo对象的x值= 40
Thread-A : 当前foo对象的x值= 40
Thread-B : 当前foo对象的x值= -20
Thread-A : 当前foo对象的x值= -20
Thread-B : 当前foo对象的x值= -80
Thread-A : 当前foo对象的x值= -80

从结果发现,这样的输出值明显是不合理的。原因是两个线程不加控制的访问Foo对象并修改其数据所致。

如果要保持结果的合理性,只需要达到一个目的,就是将对Foo的访问加以限制,每次只能有一个线程在访问。这样就能保证Foo对象中数据的合理性了。

在具体的Java代码中需要完成一下两个操作:
把竞争访问的资源类Foo变量x标识为private;
同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。

package cn.thread;public class Foo2 {private int x = 100;public int getX() {return x;}//同步方法public synchronized int fix(int y) {x = x - y;System.out.println("线程"+Thread.currentThread().getName() + "运行结束,减少“" + y+ "”,当前值为:" + x);return x;}//    //同步代码块
//    public int fix(int y) {
//        synchronized (this) {
//            x = x - y;
//            System.out.println("线程"+Thread.currentThread().getName() + "运行结束,减少“" + y
//                    + "”,当前值为:" + x);
//        }
//
//        return x;
//    }

}

package cn.thread;public class MyRunnable2  {public static void main(String[] args) {MyRunnable2 run = new MyRunnable2();Foo2 foo2=new Foo2();MyThread t1 = run.new MyThread("线程A", foo2, 10);MyThread t2 = run.new MyThread("线程B", foo2, -2);MyThread t3 = run.new MyThread("线程C", foo2, -3);MyThread t4 = run.new MyThread("线程D", foo2, 5);t1.start();t2.start();t3.start();t4.start();}class MyThread extends Thread {private Foo2 foo2;/**当前值*/private int y = 0;MyThread(String name, Foo2 foo2, int y) {super(name);this.foo2 = foo2;this.y = y;}public void run() {foo2.fix(y);}}}

线程线程A运行结束,减少“10”,当前值为:90
线程线程C运行结束,减少“-3”,当前值为:93
线程线程B运行结束,减少“-2”,当前值为:95
线程线程D运行结束,减少“5”,当前值为:90

二、同步和锁定

1、锁的原理

Java中每个对象都有一个内置锁。

当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。

当程序运行到synchronized同步方法或代码块时该对象锁才起作用。

一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。

释放锁是指持锁线程退出了synchronized同步方法或代码块。

关于锁和同步,有一下几个要点:
1)、只能同步方法,而不能同步变量和类;
2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?
3)、不必同步类中所有的方法,类可以同时拥有同步和非同步方法。
4)、 如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等 待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。
5)、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。
6)、线程睡眠时,它所持的任何锁都不会释放。
7)、线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。
8)、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。
9)、在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。例如:
public int fix(int y) {
      synchronized (this) {
            x = x - y;
      }
      return x;
}

当然,同步方法也可以改写为非同步方法,但功能完全一样的,例如:
public synchronized int getX() {
      return x++;
}

public int getX() {
      synchronized (this) {
            return x++;
      }
}
效果是完全一样的。

三、静态方法同步

要同步静态方法,需要一个用于整个类对象的锁,这个对象就是这个类(XXX.class)。
例如:
public static synchronized int setName(String name){
      Xxx.name = name;
}
等价于
public static int setName(String name){
      synchronized(Xxx.class){
            Xxx.name = name;
      }
}

四、如果线程不能获得锁会怎么样

如果线程试图进入同步方法,而其锁已经被占用,则线程在该对象上被阻塞。实质上,线程进入该对象的的一种池中,必须在哪里等待,直到其锁被释放,该线程再次变为可运行或运行为止。

当考虑阻塞时,一定要注意哪个对象正被用于锁定:
1、调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象,则每个线程有自己的对象的锁,线程间彼此互不干预。
2、调用同一个类中的静态同步方法的线程将彼此阻塞,它们都是锁定在相同的Class对象上。
3、静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。
4、对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将彼此阻塞,在不同对象上锁定的线程将永远不会彼此阻塞。

五、何时需要同步

在多个线程同时访问互斥(可交换)数据时,应该同步以保护数据,确保两个线程不会同时修改更改它。

对于非静态字段中可更改的数据,通常使用非静态方法访问。
对于静态字段中可更改的数据,通常使用静态方法访问。

如果需要在非静态方法中使用静态字段,或者在静态字段中调用非静态方法,问题将变得非常复杂。已经超出SJCP考试范围了。

六、线程安全类

当一个类已经很好的同步以保护它的数据时,这个类就称为“线程安全的”。

即使是线程安全类,也应该特别小心,因为操作的线程是间仍然不一定安全。

七、线程同步小结

1、线程同步的目的是为了保护多个线程访问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。

分类: Java多线程编程
标签: java 多线程, java 同步, java 锁

转载于:https://www.cnblogs.com/coprince/p/5846625.html

Java多线程-线程的同步与锁相关推荐

  1. java多线程-线程的同步

    在java中要实现多线程之间的同步操作主要有如下两种方式:synchronized关键字和对象的wait()和notify()/notifyAll(); 首先来看一下synchronized关键的字的 ...

  2. Java多线程——线程的优先级和生命周期

    Java多线程--线程的优先级和生命周期 摘要:本文主要介绍了线程的优先级以及线程有哪些生命周期. 部分内容来自以下博客: https://www.cnblogs.com/sunddenly/p/41 ...

  3. java多线程 线程安全_Java中的线程安全

    java多线程 线程安全 Thread Safety in Java is a very important topic. Java provides multi-threaded environme ...

  4. Java 多线程线程安全(面试概念解答二)

    Java 多线程线程安全 什么是线程安全? 为什么有线程安全问题? 线程安全解决办法? 同步代码块 同步函数 静态同步函数 多线程死锁 多线程的三大特性 原子性 可见性 有序性 Java内存模型 Vo ...

  5. java多线程同步与死锁_浅析Java多线程中的同步和死锁

    Value Engineering 1基于Java的多线程 多线程是实现并发机制的一种有效手段,它允许编程语言在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间相互独立,且与进程一样拥有独立 ...

  6. Java 多线程(三):锁(一)

    Java 多线程(三):锁(一) 作者:Grey 原文地址: 博客园:Java 多线程(三):锁(一) CSDN:Java 多线程(三):锁(一) CAS 比较与交换的意思 举个例子,内存有个值是 3 ...

  7. java多线程-线程的停止【interrupt】

    java多线程-线程的停止 文章目录 java多线程-线程的停止 线程停止的原理 如何正确停止线程 在普通情况下停止线程 代码展示 在阻塞情况下停止线程 代码展示 线程在每次迭代后都阻塞 代码展示 停 ...

  8. JAVA --- 多线程 -- 线程的创建

    JAVA - 多线程 – 线程的创建 线程的概念: 说起线程,先说程序和进程,多任务的概念. 程序(program):是指令和数据的有序集合,本身没有任何运行的含义,是一个静态的概念. 进程(proc ...

  9. java线程(2)--同步和锁

    1.线程的内存模型 Java作为平台无关性语言,JLS(Java语言规范)定义了一个统一的内存管理模型JMM(Java Memory Model),JMM屏蔽了底层平台内存管理细节,在多线程环境中必须 ...

最新文章

  1. test markdown
  2. 使用Microsoft Visual Studio安装AsmDude插件
  3. Android Studio安装Genymotion插件
  4. jQuery源码分析系列 : 整体架构
  5. os如何处理键盘的所有按键,显示or不显示,显示是如何显示
  6. AOE网的关键路径的计算
  7. agaular 离线文档_Zeal 国外一款面向开发者的离线文档查看工具
  8. colorpix取色小工具_Python版的取色器
  9. Linux常用的基本命令10
  10. 买了社保,再买农村医保是不是多余?
  11. 如果微信被运维删库、跑路,会造成什么恐怖的后果?
  12. bzoj 1799: [Ahoi2009]self 同类分布(数位DP)
  13. 第一次 Zul'grub
  14. macOS录制系统声音及麦克风的三种方法
  15. 倾斜摄影三维建模软件photoscan教程
  16. 天刀显示服务器失败,天涯明月刀手游提示安装失败怎么办 10月16日开服常见问题FAQ...
  17. PMSG孕马血清促性腺激素适用的应用方案
  18. HTML5 的新增特性
  19. Notepad++软件安装教程
  20. Android Studio将本地计算机文件上传到模拟器scard文件中,显示不出

热门文章

  1. zcmu-1181(大数相加)
  2. php smarty 限制显示字数,smarty现在显示字数的各种写法
  3. Android热修复之 - 阿里开源的热补丁
  4. java set 取第一个_set集合取第一个元素的几种方法
  5. BZOJ 4553: [Tjoi2016Heoi2016]序列
  6. java获取当前项目相对路径,在JAVA文件中获取该项目的相对路径
  7. uvalive5983(二分+dp)
  8. 笨小猴pascal题解
  9. c语言复制的代码不能运行,刚学C语言,在Linux下写的代码能正常编译,复制到VC下就无法运行...
  10. 2.4g 无线键鼠对码软件_无线路由器的2.4G和5G同时开速度有影响吗?