线程安全概念:当多个线程访问某一个类(对象或方法)时,这个对象始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
分析:当多个线程访问myThread的run方法时,以排队的方式进行处理(这里排对是按照CPU分配的先后顺序而定的),一个线程想要执行synchronized修饰的方法里的代码:1 尝试获得锁 2 如果拿到锁,执行synchronized代码体内容;拿不到锁,这个线程就会不断的尝试获得这把锁,直到拿到为止,而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题)
synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为"互斥区"或"临界区"。取得的锁都是对象锁,而不是把一段代码(方法)当做锁,所以代码中哪个线程先执行synchronized关键字的方法,哪个线程就持有该方法所属对象的锁(Lock),在静态方法上加synchronized关键字,表示锁定.class类,类一级别的锁(独占.class类)。
锁对象的改变问题:例如下面代码中,当lock发生变化后,t1开始后,t2直接开始。如果去掉将lock改变的代码,则是t1结束后t2才会开始。但是同一对象属性的修改不会影响锁的情况,比如下面代码的lock现在是一个带属性的对象,如果改变该对象的属性,结果还是t1结束后t2才会开始。
package com.bjsxt.base.sync006; /** * 锁对象的改变问题 * @author alienware * */ public class ChangeLock { private String lock = "lock"; private void method(){ synchronized (lock) { try { System.out.println("当前线程 : " + Thread.currentThread().getName() + "开始"); lock = "change lock"; Thread.sleep(2000); System.out.println("当前线程 : " + Thread.currentThread().getName() + "结束"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { final ChangeLock changeLock = new ChangeLock(); Thread t1 = new Thread(new Runnable() { @Override public void run() { changeLock.method(); } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { changeLock.method(); } },"t2"); t1.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); } }
死锁问题:在设计程序时就应该避免双方相互持有对方的锁的情况,如下代码,会发生死锁问题,t1 进入lock1执行, t2 进入lock2执行。之后,t1去等待lock2对象解锁,t2去等待lock1对象解锁,这时谁也不能解锁,程序就会停在这里一直等待,这就是死锁问题。
package com.bjsxt.base.sync006; /** * 死锁问题,在设计程序时就应该避免双方相互持有对方的锁的情况 * @author alienware * */ public class DeadLock implements Runnable{ private String tag; private static Object lock1 = new Object(); private static Object lock2 = new Object(); public void setTag(String tag){ this.tag = tag; } @Override public void run() { if(tag.equals("a")){ synchronized (lock1) { try { System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock1执行"); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2) { System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock2执行"); } } } if(tag.equals("b")){ synchronized (lock2) { try { System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock2执行"); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock1) { System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入lock1执行"); } } } } public static void main(String[] args) { DeadLock d1 = new DeadLock(); d1.setTag("a"); DeadLock d2 = new DeadLock(); d2.setTag("b"); Thread t1 = new Thread(d1, "t1"); Thread t2 = new Thread(d2, "t2"); t1.start(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); } }
volatile:用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的值。volatile很容易被误用,用来进行原子性操作。volatile关键字不具备synchronized关键字的原子性(同步)
AtomicInteger:一个提供原子操作的Integer的类。在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而AtomicInteger则通过一种线程安全的加减操作接口。注意:atomic类只保证本身方法的原子性,并不保证多次操作的原子性。比如说,我们使用AtomicInteger进行一次加十操作是具有原子性的,而同样是加十的操作,分成先加1,再加2,再加3,再加4这样四次操作,就不能保证原子性了。
脏读 :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
在线程里调用变量变量必须用final修饰
wait释放锁,notify不释放锁。配合synchronized会有不实时的问题,这时有了下面的类。
CountDownLatch:一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
主要方法
public CountDownLatch(int count); 初始化计时器数量
public void countDown();
public void await() throws InterruptedException
构造方法参数指定了计数的次数
countDown方法,当前线程调用此方法,则计数减一
await方法,调用此方法会一直阻塞当前线程,直到计时器的值为0
ThreadLocal:ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
多线程中的单例模式:
1.静态内部类--static inner class(饥汉模式)
package com.bjsxt.base.conn011; public class Singletion { private static class InnerSingletion { private static Singletion single = new Singletion(); } public static Singletion getInstance(){ return InnerSingletion.single; } }
2.双重确认--dubble check instance(懒汉模式)
package com.bjsxt.base.conn011; public class DubbleSingleton { private static DubbleSingleton ds; public static DubbleSingleton getDs(){ if(ds == null){ try { //模拟初始化对象的准备时间... Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (DubbleSingleton.class) { if(ds == null){ ds = new DubbleSingleton(); } } } return ds; } public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println(DubbleSingleton.getDs().hashCode()); } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { System.out.println(DubbleSingleton.getDs().hashCode()); } },"t2"); Thread t3 = new Thread(new Runnable() { @Override public void run() { System.out.println(DubbleSingleton.getDs().hashCode()); } },"t3"); t1.start(); t2.start(); t3.start(); } }
同步类容器(不建议使用)
并发类容器
并发Queue: