1、实现线程常见的两种的方式 :
1:继承extends Thread
2:实现new Runnable 实现其run方法
2、线程安全
结论: 当多个线程访问某一个类(对象或方法)时,这个对象始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
3、 关键字synchronized(加锁)
使用场景: 多个线程调用产生并发时。可以在任意对象及方法上加锁,而加锁的这段代码称为"互斥区"或"临界区"
原理: /**
* 分析:当多个线程访问myThread的run方法时,以排队的方式进行处理(这里排对是按照CPU分配的先后顺序而定的),
* 一个线程想要执行synchronized修饰的方法里的代码:
* 1 尝试获得锁
* 2 如果拿到锁,执行synchronized代码体内容;拿不到锁,这个线程就会不断的尝试获得这把锁,直到拿到为止,
* 而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题)
*/
使用方式:
1、对象锁
使用方式:
1、对象锁
在方法修饰符后面加 synchronized 或者方法块里面加 synchronized (this){方法块} /** * 关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当做锁, * 所以代码中哪个线程先执行synchronized关键字的方法,哪个线程就持有该方法所属对象的锁(Lock), * * 在静态方法上加synchronized关键字,表示锁定.class类,类一级别的锁(独占.class类)。 * @author alienware * */
2、 类锁
在方法里代码块加 synchronized (类.class){方法块}
定义: private Object lock = new Object(); 使用定义的对象做为锁 synchronized (lock){代码块} 注意事项:尽量不要直接使用字符串做为锁。这样有可能拿不到锁
4、对象锁线程同步和异步
1 /** 2 * 分析:method1方法加锁(synchronized) method2不加锁 3 * t1线程先持有object对象的Lock锁,t2线程可以以异步的方式调用对象中的非synchronized修饰的方法 4 * t1线程先持有object对象的Lock锁,t2线程如果在这个时候调用对象中的同步(synchronized)方法则需等待,也就是同步 5 */ 6 Thread t1 = new Thread(new Runnable() { 7 @Override 8 public void run() { 9 mo.method1(); 10 } 11 },"t1"); 12 13 Thread t2 = new Thread(new Runnable() { 14 @Override 15 public void run() { 16 mo.method2(); 17 } 18 },"t2");
5、线程原子性
原理:同一个类中,主线程main方法新开子线程 调用加锁的方法修改全局变量,在方法里打印修改后的结果。结果和修改后的一
致,在主线程打印,任是原数据,并没修改,证明,子线程修改和主线程没有任何关联。
6、synchronized的重入原理:同一个类中,主线程main方法新开子线程 调用加锁的方法修改全局变量,在方法里打印修改后的结果。结果和修改后的一
致,在主线程打印,任是原数据,并没修改,证明,子线程修改和主线程没有任何关联。
原理:调用synchronized加锁的方法。其方法体还是可以继续调synchronized加锁的方法。这就是重入(父子关系extends)重入
一样,数据保持同步。
7、锁对象的改变问题
原理:使用全局变量的对象做为锁。新建两个线程。都调用了使用了全局变量加锁的方法。只要在synchronized(对象){代码块},
代码块修改全局变量的值 ,则对象锁发生了改变。
解决方案:同一对象属性的修改不会影响锁的情况 在线程启动时synchronized方法外修改对象,对象锁不影响 还是同一把锁
8、使用synchronized代码块减小锁的粒度,提高性能。也就是在代码块加锁。