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

参考转载:http://rainyear.iteye.com/blog/1734311

http://turandot.iteye.com/blog/1704027

http://www.cnblogs.com/fguozhu/articles/2657904.html

http://lavasoft.blog.51cto.com/62575/99155

1.线程的内存模型

Java作为平台无关性语言,JLS(Java语言规范)定义了一个统一的内存管理模型JMM(Java Memory Model),JMM屏蔽了底层平台内存管理细节,在多线程环境中必须解决可见性和有序性的问题。JMM规定了jvm有主内存(Main Memory)和工作内存(Working Memory) ,主内存存放程序中所有的类实例、静态数据等变量,是多个线程共享的,而工作内存存放的是该线程从主内存中拷贝过来的变量以及访问方法所取得的局部变量,是每个线程私有的其他线程不能访问,每个线程对变量的操作都是以先从主内存将其拷贝到工作内存再对其进行操作的方式进行,多个线程之间不能直接互相传递数据通信,只能通过共享变量来进行。

重要的图片看三遍,从三个内存模型的文章中摘出的图片含义是一致的。即:

1.所有线程共享主内存

2.每个线程有自己的工作内存

需要注意的是,首先你得明白什么是主内存,主内存就是我们平时所说的内存。那么哪些变量是共享的?答类变量(静态变量),实例变量(成员变量)共享,是不安全的。而局部变量即方法体内的变量是不共享的,局部变量是安全的。

为什么会线程不安全?从上面的介绍可以看出每个线程从主内存里拿数据,改变了数据后放回主内存。当多个线程都改变主内存里的变量,这个变量的值就不确定了。再准确的说,线程1只想变量a加1,第二次取出a的时候发现a并不是自己想要的。这就是不安全!

2.什么是多线程

上一节已经学习了线程,多线程就是多个运行的线程。看起来解释很搞笑,但我觉得多线程并没有那么复杂,不要以为安全问题就头大,多线程不一定是线程不安全的。上面已经说到,多个线程共享同一个变量就会出现线程安全问题,相反的,不出现共享变量的情况下就没问题了。我有一次测试多线程没问题,后来发现我测试中没有共享变量,每个线程的主体都是新建的对象,于是不存在安全问题。然而,平时用到的多是共享的。即,多个线程的参数是同一个实例。

3.同步上锁

3.1什么是上锁

想要同步就必须要上锁,只有锁住以后,别人才不可以访问我用的东西,我释放了锁后别人才可以用,这样就保证了我使用范围内的变量的绝对控制,即线程安全,也就是同步。那么什么是锁?

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

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

看完介绍,明白:

  • 对象有个锁,通过synchronize获取;
  • 对象只有一个锁;
  • 对象锁住后别的线程不能访问synchronize代码块;
  • 锁是针对对象的;
  • this表示当前对象

3.2方法上锁

下面是容易理解和看到的例子,就是在方法头加上关键字synchronized

    public synchronized void setName(String name){
        this.name = name;
    }

3.3对象上锁

对象上锁用this,this代表当前对象。

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

3.4静态方法上锁

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

3.5如果线程得不到锁会怎样

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

当考虑阻塞时,一定要注意哪个对象正被用于锁定:

1、调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象,则每个线程有自己的对象的锁,线程间彼此互不干预。

2、调用同一个类中的静态同步方法的线程将彼此阻塞,它们都是锁定在相同的Class对象上。

3、静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。

4、对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将彼此阻塞,在不同对象上锁定的线程将永远不会彼此阻塞。

看介绍明白:上锁一定是对象的锁。

3.6死锁

死锁,很熟悉的名字。死锁是线程互相等待,a需要b的资源,但b的资源被b持有没有释放,a阻塞等待;b需要a的资源,但a的资源被a持有没有释放,b阻塞等待。就是我等你,你等我,死循环。实例:

package com.test.java.thread;

/**
 * 死锁
 * Created by mrf on 2016/2/26.
 */
public class DeadLockRisk {
    private static class Resource {
        public int value;
    }

    private Resource resourceA = new Resource();
    private Resource resourceB = new Resource();

    public int read() {
        synchronized (resourceA) {
            synchronized (resourceB) {
                System.out.println("read");
                return resourceB.value + resourceA.value;
            }
        }
    }

    public void write(int a, int b) {
        synchronized (resourceB) {
            synchronized (resourceA) {
                System.out.println("write");
                resourceA.value = a;
                resourceB.value = b;
            }
        }
    }

}

class DeadLockRun implements Runnable{
    DeadLockRisk deadLockRisk = new DeadLockRisk();
    @Override
    public void run() {
        deadLockRisk.write(1,2);
        deadLockRisk.read();
    }

    public static void main(String[] args) {
        DeadLockRun deadLockRun = new DeadLockRun();
        Thread t1 = new Thread(deadLockRun);
        Thread t2 = new Thread(deadLockRun);
        t1.start();
        t2.start();
    }
}

这个实例中,要注意到两个线程的声明过程,都是针对同一个对象的,所以才有资源争抢行为。

4小结

1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。

2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。

3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。

4、对于同步,要时刻清醒在哪个对象上同步,这是关键。

5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。

6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。

7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。

还有,同步通过上锁来实现,即原子操作互不影响;上锁是针对对象的,类对象或者实例对象。

时间: 2024-12-21 13:44:11

java线程(2)--同步和锁的相关文章

Java线程:线程的同步与锁

一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. public class Foo {     private int x = 100; public int getX() {         return x;     } public int fix(int y) {         x = x - y;         return x;     } }

Java线程与同步

手机平台的自动化测试工具很多,之前研究过了安卓和苹果的原生自动化测试框架,经一些同事介绍,貌似Appium是个不错的工具. 想记录一下研究的结果,也算是篇干货的文章 在网上也看了一些视频,个人认为这个自动化测试的特点就是 1. 多编程语言支持 相对于传统的IOS UIautomation只能支持js语言,Appium起到了一个翻译的作用,它其实是一个CS架构,服务器和IOS模拟器或真机直接通讯,客户端和服务器之间用HTTP协议进行交互,所以客户端用什么语言其实不重要.下面会有一个python编程

Java线程:线程的同步与锁

1.同步和锁 java中每个对象都有一个内置锁,程序执行非静态的synchronized同步方法时,自动获得执行该方法的对象有关的锁. 一个对象只有一个锁,当一个线程获得锁,其他线程不能进入该对象上的synchronized方法或代码块.直到锁被释放. 线程可以有多个锁,例如,一个对象的同步方法中调用另一个对象的同步方法 2.静态方法的同步 同步静态方法,用的是类对象的锁,即xx.class. 3.线程安全类 对类中的方法进行了同步,但操作这个类时仍然不一定安全. 例如: public  cla

Java多线程-线程的同步与锁

一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏.例如:两个线程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;

Java多线程-线程的同步与锁【转】

出处:http://www.cnblogs.com/linjiqin/p/3208843.html 一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. package cn.thread; public class Foo { private int x = 100

Java线程:同步

一 同步的概念 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. MyRunnable.java 1 package Thread; 2 public class MyRunnable implements Runnable{ 3 private Foo foo=new Foo(); 4 public static void main(String[] args){ 5 MyRu

[ 转载 ] Java多线程-线程的同步与锁

http://www.cnblogs.com/linjiqin/p/3208843.html 原文地址:https://www.cnblogs.com/ILoke-Yang/p/8137374.html

java线程的同步

问题的引出 在java语言中,我们常常会用到多线程相关的操作,但是多线程操作中可能会出现一些问题. 现在给定一个抢票的多线程代码 class MyThread implements Runnable{ int a = 10;//票数 @Override public void run() { while(true) { if(a>0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(

Java深度历险(三)——Java线程?:基本概念、可见性与同步

开发高性能并发应用不是一件容易的事情.这类应用的例子包括高性能Web服务器.游戏服务器和搜索引擎爬虫等.这样的应用可能需要同时处理成千上万个请求.对于这样的应用,一般采用多线程或事件驱动的架构.对于Java来说,在语言内部提供了线程的支持.但是Java的多线程应用开发会遇到很多问题.首先是很难编写正确,其次是很难测试是否正确,最后是出现问题时很难调试.一个多线程应用可能运行了好几天都没问题,然后突然就出现了问题,之后却又无法再次重现出来.如果在正确性之外,还需要考虑应用的吞吐量和性能优化的话,就