2.7 线程锁

  • synchronized

  • <mark>Lock</mark>(更快)

    • ReentrantLock
    • ReentrantReadWriteLock
      • ReadLock
      • WriteLock
    • 方法:
      • lock()
      • unlock()
  • 重入 reentrant

    • 调用同一锁定代码,可以再次进入
    • synchronized 可重入
    • ReentrantLock 可重入
  • <mark>读</mark>锁和<mark>写</mark>锁

    • <mark>读</mark>锁是一种<mark>共享</mark>锁
      • 可以被多个线程同时,同时访问数据
      • 可以提高访问数据的并发性能
    • <mark>写</mark>锁
      • 排他锁,<mark>只能被一个线程</mark>获得
  • Lock 底层原理

    • 原子操作工具
      • AtomicInteger
      • AtomicLong
      • AtomicReference
    • CAS
      • Compare And Swap
      • CAS算法,用非阻塞的方式,来获得锁
    • 自旋锁
      • 占用cpu资源
      • 用忙循环,用CAS来获得锁


2.7.1 悲观锁和乐观锁

  • <mark>悲观锁</mark>:还是像它的名字一样,对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不管三七二十一,直接上了锁就操作资源了。

  • <mark>乐观锁</mark>:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较-替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。




2.7.2 两种常见的锁

  • <mark>Synchronized 互斥锁(悲观锁,有罪假设)</mark>

    • 采用synchronized修饰符实现的同步机制叫做互斥锁机制,它所获得的锁叫做互斥锁。每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。任何一个对象系统都会为其创建一个互斥锁,这个锁是为了分配给线程的,防止打断原子操作。每个对象的锁只能分配给一个线程,因此叫做互斥锁。
  • <mark>ReentrantReadWriteLock 读写锁(乐观锁,无罪假设)</mark>

    • ReentrantLock是排他锁,排他锁在同一时刻仅有一个线程可以进行访问,实际上独占锁是一种相对比较保守的锁策略,在这种情况下任何“读/读”、“读/写”、“写/写”操作都不能同时发生,这在一定程度上降低了吞吐量。然而读操作之间不存在数据竞争问题,如果”读/读”操作能够以共享锁的方式进行,那会进一步提升性能。因此引入了ReentrantReadWriteLock,顾名思义,ReentrantReadWriteLock是Reentrant(可重入)Read(读)Write(写)Lock(锁),我们下面称它为读写锁。
    • 读写锁内部又分为读锁和写锁,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。读锁和写锁分离从而提升程序性能,读写锁主要应用于读多写少的场景。

两种“锁的对比”

package cn.edut.com.tarena;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Test04_reentrantLock {
	static char[] a = {'*','*','*','*','*','*','*','*','*'};
	static ReentrantReadWriteLock lock = new ReentrantReadWriteLock() ;
	static char c = '-';
	public static void main(String[] args) {
		new  Thread() {
			@Override
			public void run() {
				while(true) {
					//synchronized (a) {
						
					lock.writeLock().lock(); 
						for(int i=0 ; i<a.length ; i++) {
							a[i] = c ; 
						}
						c = c=='*'?'-':'*';
					lock.writeLock().unlock();
					//}
				}
			}
		}.start();
		new Thread() {
			@Override
			public void run() {
				while(true) {
					//synchronized (a) {
						lock.readLock().lock(); 
						System.out.println(a);
						lock.readLock().unlock();
					//}
				}
			}
		}.start();
	}
}

synchronize 测试

RenntrantWriteReadLock 测试






<mstyle mathcolor="&#35;ff0011"> </mstyle> \color{#ff0011}{**学反射前知识**}

.class在方法中存储的数据,的调用名称:

  • Field = 》 成员变量
  • Constructor =》 构造方法
  • Method = 》 成员方法

1.1 反射 Reflect

文本配置文件


day15.A;a

day15.B;b

day15.C;c


要根据配置文件配置的流程,来执行

加载到方法区的“类对象”的反射操作,可以

  • 获得一个类的定义信息
  • 反射新建实例
  • 反射调用成员

1.1.1 获取“类对象”

  • 通过Class类获取:Class.forName(“day1801.A”)
  • 通过已加载类获取:A.class
  • 通过实例获取:a1.getClass()

forName 测试

package cn.edut.com.tarena;

public class Test05_forName {
	public static void main(String[] args) throws Exception {
		String name = "cn/edut/com/tarena/Test00" ;
		name = name.replace("/" , "." ) ;
		Class<?> test = Class.forName(name);
		System.out.println(test.getName());
	}
	
}




1.1.3 获取<mark>成员变量</mark>定义<mark>信息</mark>

获得所有公开的成员变量,包括继承的变量
getFields()

获得本类定义的成员变量,包括私有
不包括继承的变量
getDeclaredFields()

getField(变量名)

getDeclaredField(变量名)

1.1.4 获取<mark>构造方法</mark>定义<mark>信息</mark>

获得所有公开的构造方法
getConstructors()

获得所有构造方法,包括私有
getDeclaredConstructors()

getConstructor(参数类型列表)

getDeclaredConstructor(int.class, String.class)
<mark>指定参数依次为 int 和 String 的构造方法</mark>

1.1.5 获取<mark>方法</mark>定义<mark>信息</mark>

获得所有可见的方法,包括继承的方法
getMethods()

获得本类定义的方法,包括私有
不包括继承的方法
getDeclaredMethods()

getMethod(方法名,参数类型列表)

getDeclaredMethod(方法名, int.class, String.class)

package cn.edut.com.tarena;

public class Test00 {
	public int a = 100; 
	protected String b = "adfa"; 
	Integer c = new Integer(60);
	private  D<D<Integer>> d   = new D<>();
	
	public Test00() {
	}
	public Test00(int a ) {
		this.a = a ; 
	}
	public Test00(String b , int a ) {
	}
	public Test00(int a , D<Integer> c) {
	}
	
	
	private void a(int a ) {};
	protected String b(String c) {return "bbb";}
	void c() {};
	public D d() {return new D();}
	
}

class D<T> {}

测试

package cn.edut.com.tarena;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

public class Test05_forName {
	public static void main(String[] args) throws Exception {
		String name = "cn/edut/com/tarena/Test00" ;
		name = name.replace("/" , "." ) ;
		Class<?> test = Class.forName(name);
		System.out.println("---包名类名-----------------");
		showPackage(test);		
		System.out.println("---成员变量名-----------------");
		showFields(test);
		System.out.println("---构造方法-----------------");
		showConstructor(test);
		System.out.println("---包名类名-----------------");
		showMethod(test) ; 
	}
	
	
	
	private static void showMethod(Class<?> test) {
		Method[] methods = test.getDeclaredMethods();
		for(Method m : methods ) {
			System.out.println(m);
		}
	}



	private static void showConstructor(Class<?> test) {
		Constructor<?>[] constructors = test.getDeclaredConstructors();
		for(Constructor<?> c : constructors) {
			String className = test.getSimpleName();
			System.out.println(className+" "+Arrays.toString(c.getParameters()));
			System.out.println(c.toString());
			System.out.println("。 。 。 。 。 。 ");
		}
	}

	private static void showFields(Class<?> test) {
		/* * Field封装成员变量的定义信息 * * private static final int a */
		Field[] a = test.getDeclaredFields();
		for (Field f : a) {
			String type = f.getType().getSimpleName();
			String name = f.getName();
			System.out.println(type+" "+name);
			System.out.println(f);
			System.out.println("。 。 。 。 。 。 ");
		}
	}

	private static void showPackage(Class<?> test) {
		System.out.println(test.getName());
	}
	
}

结果:

—包名类名-----------------
cn.edut.com.tarena.Test00
—成员变量名-----------------
int a
public int cn.edut.com.tarena.Test00.a
。 。 。 。 。 。
String b
protected java.lang.String cn.edut.com.tarena.Test00.b
。 。 。 。 。 。
Integer c
java.lang.Integer cn.edut.com.tarena.Test00.c
。 。 。 。 。 。
D d
private cn.edut.com.tarena.D cn.edut.com.tarena.Test00.d
。 。 。 。 。 。
—构造方法-----------------
Test00 [int arg0, cn.edut.com.tarena.D<java.lang.Integer> arg1]
public cn.edut.com.tarena.Test00(int,cn.edut.com.tarena.D)
。 。 。 。 。 。
Test00 [java.lang.String arg0, int arg1]
public cn.edut.com.tarena.Test00(java.lang.String,int)
。 。 。 。 。 。
Test00 [int arg0]
public cn.edut.com.tarena.Test00(int)
。 。 。 。 。 。
Test00 []
public cn.edut.com.tarena.Test00()
。 。 。 。 。 。
—包名类名-----------------
void cn.edut.com.tarena.Test00.c()
protected java.lang.String cn.edut.com.tarena.Test00.b(java.lang.String)
private void cn.edut.com.tarena.Test00.a(int)
public cn.edut.com.tarena.D cn.edut.com.tarena.Test00.d()

1.1.6 反射<mark>新建实例</mark>

  • 新建实例时,执行<mark>无参构造</mark>

Object obj = c.newInstance();

  • 新建实例时,执行<mark>有参构造</mark>
    获取构造方法
  • 新建实例,并执行该构造方法

Constructor t = c.getConstructor(int.class, String.class);
Object obj = t.newInstance(6, "abc");

package cn.edut.com.tarena;

public class Test00 {
	public int a = 100; 
	protected String b = "adfa"; 
	private Integer c = new Integer(60);
	D<D<Integer>> d   = new D<>();
	
	public Test00() {
	}
	public Test00(int a ) {
		this.a = a ; 
	}
	public Test00(String b , int a ) {
	}
	public Test00(int a , D<Integer> c) {
	}
	
	
	protected void a(int a ) {};
	private String b(String c) {return c+"d";}
	void c() {};
	public D d() {return new D();}
	
}

class D<T> {}

测试方法
package cn.edut.com.tarena;

import java.lang.reflect.Constructor;

public class Test06_newInstance {
	public static void main(String[] args) throws Exception {
		System.out.println("---无参构造-----");
		unparaConstructor();
		System.out.println("---无参构造-----");
		Constructor<Test00> constructor = Test00.class.getConstructor(int.class); 
		Test00 newInstance = constructor.newInstance(123456789);
		System.out.println("a="+newInstance.a);
		
	}

	private static void unparaConstructor() throws Exception {
		Test00 obj = Test00.class.newInstance();
		System.out.println("a=" + obj.a);
	}
	
}




1.1.7 反射调用<mark>成员变量</mark>

获取变量
Field f = c.getDeclaredField(变量名);

使私有成员允许访问
f.setAccessible(true);

反射给变量赋值
为指定实例的变量赋值。<mark>静态变量,第一参数给 null</mark>
f.set(实例, 值);

反射访问变量的值
访问指定实例的变量的值。<mark>静态变量,第一参数给 null</mark>
Object v = f.get(实例);


package cn.edut.com.tarena;

import java.lang.reflect.Field;

public class Test07_para {
	public static void main(String[] args) throws Exception {
		Class<Test00> s = Test00.class;
		Test00 t1 = s.newInstance();
		
		//获取变量
		Field c = s.getDeclaredField("c");
		c.setAccessible(true);
		System.out.println("c="+c.get(t1));
		c.set(t1, 99999);
		System.out.println("c="+c.get(t1));
		
	}
}




1.1.8 反射调用成员方法

获取方法
Method m = c.getDeclaredMethod(方法名, 参数类型列表);

使私有方法允许被调用
m.setAccessible(true)

反射调用方法
让指定的实例来执行该方法
Object returnValue = m.invoke(实例, 参数数据)

package cn.edut.com.tarena;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Test08_method {
	public static void main(String[] args) throws Exception {
		//class类
		Class<Test00> t = Test00.class ;
		//含参构造
		Constructor<Test00> constructor1 = t.getConstructor(int.class);
		//实例
		Test00 test1 = constructor1.newInstance(100); 
		
		Method b_Method = t.getDeclaredMethod("b", String.class);
		b_Method.setAccessible(true);
		Object result = b_Method.invoke(test1, "abc");
		System.out.println(result);
	}
}