多线程--对象及变量的并发访问

1 . 多个线程访问多个对象JVM会创建多个锁。
2 . 静态方法是以类为单位进行同步的——对于同一个类中的所有静态方法,在同一时间内,只允许有一个线程执行其中的一个静态方法,其余想要进入这些方法的线程都必须挂起等待。非静态方法是以对象为单位进行同步的。
3 .假设现有两个线程A和B,一个object对象,当线程A调用object对象的一个同步方法M1时,线程A就获得了M1方法所在对象的锁,所以其他线程必须等待线程A执行完毕之后才能调用方法M1,如果线程B调用object的同步方法M2,必须等待线程A将M1方法执行完 ,也就是释放对象锁后才可以调用 ;但是线程B可以随意调用其他其他的非同步方法。
4 .synchronized具有锁重入(自己可以再次获取自己的内部锁)的功能:当一个线程得到一个对象锁后再次请求此对象锁时可以再次得到该对象的锁。即在一个同步方法/块内部调用本类的其他同步方法/块是永远可以得到锁的。可重入锁也支持在父子类继承的关系中即子类可以通过可重入锁调用父类的同步方法。

/**
 * 此类用于演示锁重入的概念
 *
 * 可重入锁:自己可以再次获取自己的内部锁。例如一个线程获得了某个对象的锁,此时这个对象还没有释放,当其再次想要获取这个对象的锁的时候还是可以获得,
 * 如果不可锁重入的话,则会造成死锁,并且当存在父子集成关系时,子类完全可以通过"可重入锁"调用父类的同步方法
 *
 * @Description
 * @author niepei
 * @date 2017年4月25日 下午3:52:41
 * @version V1.3.1
 */
public class LockReentry {
    public synchronized void service1() {
        System.out.println("service1");
        this.service2();
    }

    public synchronized void service2() {
        System.out.println("service2");
        this.service3();
    }

    public synchronized void service3() {
        System.out.println("service3");
    }

    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                LockReentry service = new LockReentry();
                service.service1();
            }
        };
        thread.start();
    }
}

5 . 当一个线程执行的代码出现异常,其持有的锁会自动释放
6 .同步不能被继承,比如父类的service()方法是同步的,子类继承了父类,子类中的service()方法需要声明为synchronized。
7 . 当使用synchronized代码块时需要注意,如果一个线程访问object的一个同步代码块,其他线程对同一个object的所有其他的同步代码块的的访问将被阻塞
8 . 多线程的死锁:当双方互相持有对方的锁的时候就会出现死锁,即只要互相等待对方释放锁就有可能出现死锁。下面这段代码为线程死锁示例:

public class DeadThread implements Runnable {

    private String username;

    private Object lock1 = new Object();

    private Object lock2 = new Object();

    public void setFlag(String username) {
        this.username = username;
    }

    @Override
    public void run() {
        if (username.equals("a")) {
            synchronized (lock1) {
                System.out.println("username = " + username);
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }

                synchronized (lock2) {
                    System.out.println("按 lock1 -> lock2 代码顺序执行了");
                }
            }
        }
        if (username.equals("b")) {
            synchronized (lock2) {
                System.out.println("username = " + username);
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }

                synchronized (lock1) {
                    System.out.println("按 lock2 -> lock1 代码顺序执行了");
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        DeadThread dt = new DeadThread();

        dt.setFlag("a");
        Thread a = new Thread(dt);
        a.setName("A");
        a.start();

        Thread.sleep(1000);

        dt.setFlag("b");
        Thread b = new Thread(dt);
        b.setName("B");
        b.start();
    }

}

运行结果为:
username = a
username = b

9 . volatile关键字的作用是使实例变量在多个线程间可见,可以强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。

  • volatile 是线程同步的轻量级实现,所以性能肯定比synchronized好,并且volatile只能用于修饰变量,而synchronized可以修饰方法,代码块
  • 多线程访问volatile不会发生阻塞,而synchronized会出现阻塞
  • volatile能保证数据的可见性,但是不能保证原子性,而synchronized既能保证原子性也能间接保证可见性(通过将私有内存和公共内存的数据做同步实现)
  • volatile解决的是变量在多个线程之间的可见性,而synchronized解决的是多个线程之间访问资源的同步性
  • 线程安全包括原子性和同步性,java的同步机制都是围绕这两个方面来确保线程安全的

10 .synchronized关键字可以保证在同一时刻只有一个线程可以执行某个方法或者或者某段代码块,他包含两个特征:互斥性和可见性。synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前所有的修改结果

11.类ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程可以执行lock后面的代码,这样虽然保证了实例变量的安全性但是效率比较低,未解决这个问题可以使用ReentrantReadWriteLock类,在某些不需要使用实例变量的方法中可以使用ReentrantReadWriteLock来提高下效率.读写锁也有两个锁,一个读锁(共享锁)一个写锁(互斥锁)。读读共享,读写互斥,写写互斥。即多个Thread可以同时进行读操作,但同一时刻只有一个线程可以进行写操作。

package cn.com.thread;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLock {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void read() {
        try {
            lock.readLock().lock();
            System.out.println(Thread.currentThread().getName() + "在" + System.currentTimeMillis() / 1000 + "时刻获得了读锁");
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.readLock().unlock();
        }
    }

    public void write() {
        try {
            lock.writeLock().lock();
            System.out.println(Thread.currentThread().getName() + "在" + System.currentTimeMillis() / 1000 + "时刻获得了写锁");
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.writeLock().unlock();
        }
    }

    public static void main(String[] args) {
        //读读共享
        testReadReadLock();
        /**
         * 打印结果如下:
         * R1在1493257634时刻获得了读锁
         * R2在1493257634时刻获得了读锁
         */

        //读写互斥
        testReadWriteLock();
        /**
         * 打印结果如下:
         * R在1493258342时刻获得了读锁
         * W在1493258343时刻获得了写锁
         */

        //写写互斥
        testWriteWriteLock();
        /**
         * 打印结果如下:
         * W1在1493259822时刻获得了写锁
         * W2在1493259823时刻获得了写锁
         */
    }

    public static void testReadReadLock() {
        ReadWriteLock lock = new ReadWriteLock();
        new ReadThread("R1",lock).start();
        new ReadThread("R2",lock).start();
    }

    public static void testReadWriteLock() {
        ReadWriteLock lock = new ReadWriteLock();
        new ReadThread("R",lock).start();
        new WriteThread("W",lock).start();
    }

    public static void testWriteWriteLock() {
        ReadWriteLock lock = new ReadWriteLock();
        new WriteThread("W1",lock).start();
        new WriteThread("W2",lock).start();
    }

}
/**
 * 读线程
 * @Description
 * @author niepei
 * @date 2017年4月27日 上午10:05:57
 * @version V1.3.1
 */
class ReadThread extends Thread {
    private ReadWriteLock lock;

    public ReadThread(String name, ReadWriteLock lock) {
        super(name);
        this.lock = lock;
    }

    @Override
    public void run() {
        lock.read();
    }
}
/**
 * 写线程
 * @Description
 * @author niepei
 * @date 2017年4月27日 上午10:05:46
 * @version V1.3.1
 */
class WriteThread extends Thread {
    private ReadWriteLock lock;

    public WriteThread(String name, ReadWriteLock lock) {
        super(name);
        this.lock = lock;
    }

    @Override
    public void run() {
        lock.write();
    }
}
时间: 2024-12-10 00:23:59

多线程--对象及变量的并发访问的相关文章

java多线程(对象和变量的并发访问)

在现实开发中,我们写的线程肯定会有不同的实例在执行,此时就可能会出现"非线程安全问题",非线程安全就是:多个线程对同一个对象中的实例变量进行并发访问时候,有可能A和B线程同时读取到数据,先后进行更改,此时,该变量就不是我们期望的数据,也就是通常所说的"脏数据" 实例变量非线程安全 需要注意的是,方法中的变量是不存在非线程安全问题的,这是因为方法内部的变量都是私有的. 如果多个线程共同访问了一个对象中的实例变量,则可能会出现线程安全问题.看下面代码: public c

Java多线程编程核心 - 对象及变量的并发访问

1.什么是“线程安全”与“非线程安全”? “非线程安全”会在多个线程对同一对象总的实例变量进行并发访问时发生,产生的后果是“脏读”,也就是取到的数据其实是被更改过的. “线程安全”是以获得的实例变量的值是经过同步处理的,不会出现脏读的现象. 2.非线程安全例子?怎么解决? 非线程安全 package com.jvm.thread; public class HasSelfPrivateNum { private int num =  0; public void add(String usern

(二)对象以及变量的并发访问--synchronized的使用细节,用法

具体的记录synchronized关键的各种使用方式,注意事项.感觉一步一步跟我来都可以看懂滴 大致是按照以下思路进行书写的.黑体字可以理解为结论, 1.synchronized锁的是什么? 2.synchronized能够锁住所有方法吗? 3.synchronized能够用来锁住一个方法之中的部分代码吗? 4.synchronized能够锁住除了this以外的其他对象吗?有什么用?有什么需要注意的? -----------------------------------------------

Java多线程编程(二)对象及变量的并发访问

一.synchronized同步方法 1.方法内的变量为线程安全 2.实例变量非线程安全 3.多个对象多个锁 4.synchronized方法与锁对象 5.脏读 6.synchronized锁冲入 7.出现异常,锁自动释放 8.同步不具有继承性 二.synchronized同步语句块 1.synchronized方法的弊端 2.synchronized同步代码块的使用 3.用同步代码块解决同步的弊端 4.一半异步,一半同步 5.synchronized代码块间的同步性 6.验证同步synchro

第二章:对象及变量的并发访问

为什么要使用多线程编程?什么时候会出现线程安全问题? 在单线程中不会出现线程安全问题,而在多线程编程中,有可能会出现同时访问同一个资源的情况,这种资源可以是各种类型的的资源:一个变量.一个对象.一个文件.一个数据库表等,而当多个线程同时访问同一个资源的时候,就会存在一个问题: 由于每个线程执行的过程是不可控的,所以很可能导致最终的结果与实际上的愿望相违背或者直接导致程序出错. 举个简单的例子: 现在有两个线程分别从网络上读取数据,然后插入一张数据库表中,要求不能插入重复的数据. 那么必然在插入数

第二章 对象以及变量的并发访问

synchronied 对象监视器为Object时的使用,或者监视器为Class时的使用. 方法中的变量不存在非线程安全问题,永远都是线程安全的,这是方法内部的变量是私有的特性造成的. 1 synchronized的使用 在方法前加关键字synchronized即可. 1)A线程先持有object对象的Lock锁,B线程可以异步的方式调用object对象中的非synchrionized类型的方法. 2)A线程先持有object对象的Lock锁,B线程如果在这时调用了object对象中的synch

对象及变量的并发访问一

一.多个线程操作一个对象实例 当两个线程同时访问一个没有同步的方法,如果两个线程同时操作业务对象中的实例变量,则有可能会出现“非线程安全问题”. 1 package concurrent; 2 /** 3 * 测试不同线程操作同一个实例变量线程安全问题 4 * @author foolishbird_lmy 5 * 6 */ 7 class ThePrivateNumber{ 8 private int num = 0; 9 10 public synchronized void addI(St

第二章:对象及变量的并发序言

本章主要介绍了java多线程中的同步,也就是如何在java语言中写出线程安全的程序. 如何在java语言中解决非线程安全的相关问题. 设计知识点: synchronized对象监视器为Object时的使用. synchronized对象监视器为Class时的使用. 非线程安全是如何出现的. 关键字volatile的主要作用. 关键字volatile与synchronized的区别及使用情况.

Cocos2d-x优化中多线程并发访问

多线程并发访问在Cocos2d-x引擎中用的不是很多,这主要是因为中整个结构设计没有采用多线程.源自于Objective-C的Ref对象,需要使用AutoreleasePool进行内存管理,AutoreleasePool是非线程安全的,所有不推荐在子多线程中调用Ref对象的retain(). release()和autorelease()等函数.另外,OpenGL上下文对象也是不支持线程安全的.但是有的时候我们需要异步加载一些资源,例如:加载图片纹理.声音的预处理和网络请求数据等.如果是异步加载