Java线程synchronized(一)
线程安全概念:当多个线程访问某一个类(对象或方法)时,这个对象始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为"互斥区"或"临界区"
public class MyThread extends Thread{ private int count = 5 ; public void run() { count--; System.out.println(this.currentThread().getName() + " count = " + count); } public static void main(String[] args) { /** * 分析:当多个线程访问myThread的run方法时,以排队的方式进行处理(这里排对是按照CPU分配的先后顺序而定的), * 一个线程想要执行synchronized修饰的方法里的代码: * 1 尝试获得锁 * 2 如果拿到锁,执行synchronized代码体内容;拿不到锁,这个线程就会不断的尝试获得这把锁,直到拿到为止, * 而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题) */ MyThread myThread = new MyThread(); Thread t1 = new Thread(myThread,"t1"); Thread t2 = new Thread(myThread,"t2"); Thread t3 = new Thread(myThread,"t3"); Thread t4 = new Thread(myThread,"t4"); Thread t5 = new Thread(myThread,"t5"); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
同步:synchronized 同步的概念就是共享锁,不是共享的资源就没必要进行同步。
异步:asynchronized 相互之间不受到制约。
同步的目的就是为了线程安全,需要满足两个特性:
1.原子性
2.可见性
对象锁的同步和异步问题
public class MyObject { public synchronized void method1(){ try{ System.out.println(Thread.currentThread().getName()); Thread.sleep(4000); }catch (InterruptedException e){ e.printStackTrace(); } } public void method2(){ System.out.println(Thread.currentThread().getName()); } public static void main(String[] args) { final MyObject mo=new MyObject(); /** * 分析: * t1线程先持有object对象的Lock锁,t2线程可以以异步的方式调用对象中的非synchronized修饰的方法 * t1线程先持有object对象的Lock锁,t2线程如果在这个时候调用对象中的同步(synchronized)方法则需等待,也就是同步 */ Thread t1=new Thread(new Runnable() { @Override public void run() { mo.method1(); } },"t1"); Thread t2=new Thread(new Runnable() { @Override public void run() { mo.method2(); } },"t2"); t1.start(); t2.start(); } }
对于对象的同步和异步方法,设计程序,一定要考虑问题的整体,不然就会出现数据不一致的情况。脏读(dirtyread)
public class DirtyRead { private String username = "laoshi"; private String password = "123"; public synchronized void setValue(String username, String password){ this.username = username; try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } this.password = password; System.out.println("setValue最终结果:username = " + username + " , password = " + password); } public void getValue(){ System.out.println("getValue方法得到:username = " + this.username + " , password = " + this.password); } public static void main(String[] args) throws Exception{ final DirtyRead dr = new DirtyRead(); Thread t1 = new Thread(new Runnable() { @Override public void run() { dr.setValue("laosun", "456"); } }); t1.start(); Thread.sleep(1000); dr.getValue(); } }
要枷锁,一起枷锁。不加都不加,保证业务的原子性。
synchronized锁重入:
当一个线程得到一个对象的锁后,再次请求对象可以再次得到对象的锁
public class SyncDubbo1 { public synchronized void method1(){ System.out.println("method1.."); method2(); } public synchronized void method2(){ System.out.println("method2.."); method3(); } public synchronized void method3(){ System.out.println("method3.."); } public static void main(String[] args) { final SyncDubbo1 sd = new SyncDubbo1(); Thread t1 = new Thread(new Runnable() { @Override public void run() { sd.method1(); } }); t1.start(); } }
public class SyncDubbo2 { static class Main { public int i = 10; public synchronized void operationSup(){ try { i--; System.out.println("Main print i = " + i); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } static class Sub extends Main { public synchronized void operationSub(){ try { while(i > 0) { i--; System.out.println("Sub print i = " + i); Thread.sleep(100); this.operationSup(); } } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { Sub sub = new Sub(); sub.operationSub(); } }); t1.start(); } }
synchronized异常
public class SyncException { private int i = 0; public synchronized void operation(){ while(true){ try { i++; Thread.sleep(100); System.out.println(Thread.currentThread().getName() + " , i = " + i); if(i == 20){ //Integer.parseInt("a"); throw new RuntimeException(); } } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { final SyncException se = new SyncException(); Thread t1 = new Thread(new Runnable() { @Override public void run() { se.operation(); } },"t1"); t1.start(); } }
锁对象的改变问题
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(); } }
死锁问题,在设计程序时就应该避免双方相互持有对方的锁的情况
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(); } }
同一对象属性的修改不会影响锁的情况
public class ModifyLock { private String name ; private int age ; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public synchronized void changeAttributte(String name, int age) { try { System.out.println("当前线程 : " + Thread.currentThread().getName() + " 开始"); this.setName(name); this.setAge(age); System.out.println("当前线程 : " + Thread.currentThread().getName() + " 修改对象内容为: " + this.getName() + ", " + this.getAge()); Thread.sleep(2000); System.out.println("当前线程 : " + Thread.currentThread().getName() + " 结束"); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { final ModifyLock modifyLock = new ModifyLock(); Thread t1 = new Thread(new Runnable() { @Override public void run() { modifyLock.changeAttributte("张三", 20); } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { modifyLock.changeAttributte("李四", 21); } },"t2"); t1.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); } }
使用synchronized代码块加锁,比较灵活
public class ObjectLock { public void method1(){ synchronized (this){//对象锁 try { System.out.println("do method1.."); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } public void method2(){ //类锁 synchronized (ObjectLock.class){ try { System.out.println("do method2.."); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } private Object lock=new Object(); public void method3(){ //任何对象锁 synchronized (lock){ try { System.out.println("do method3.."); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { final ObjectLock objLock = new ObjectLock(); Thread t1 = new Thread(new Runnable() { @Override public void run() { objLock.method1(); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { objLock.method2(); } }); Thread t3 = new Thread(new Runnable() { @Override public void run() { objLock.method3(); } }); t1.start(); t2.start(); t3.start(); } }
使用synchronized代码块减小锁的粒度,提高性能
public class Optimize { public void doLongTimeTask(){ try { System.out.println("当前线程开始:" + Thread.currentThread().getName() + ", 正在执行一个较长时间的业务操作,其内容不需要同步"); Thread.sleep(2000); synchronized(this){ System.out.println("当前线程:" + Thread.currentThread().getName() + ", 执行同步代码块,对其同步变量进行操作"); Thread.sleep(1000); } System.out.println("当前线程结束:" + Thread.currentThread().getName() + ", 执行完毕"); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { final Optimize otz = new Optimize(); Thread t1 = new Thread(new Runnable() { @Override public void run() { otz.doLongTimeTask(); } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { otz.doLongTimeTask(); } },"t2"); t1.start(); t2.start(); } }
synchronized代码块对字符串的锁,注意String常量池的缓存功能
public class StringLock { public void method(){ synchronized ("字符串常量") { try { while(true){ System.out.println("当前线程 : " + Thread.currentThread().getName() + "开始"); Thread.sleep(1000); System.out.println("当前线程 : " + Thread.currentThread().getName() + "结束"); } } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { final StringLock stringLock = new StringLock(); Thread t1 = new Thread(new Runnable() { @Override public void run() { stringLock.method(); } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { stringLock.method(); } },"t2"); t1.start(); t2.start(); } }