线程安全概念:当多个线程访问某一个类(对象或方法)时,这个对象始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
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(); } }