多线程--对象及变量的并发访问
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(); } }