1.同一個資源,同步和非同步的方法可以同時調用
package com.dingyu; public class Y { public synchronized void m1() { System.out.println(Thread.currentThread().getName() + " m1 begin---------"); try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " m1 end---------"); } public void m2() { System.out.println(Thread.currentThread().getName() + " m2 begin---------"); try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " m2 end---------"); } public static void main(String[] args) { Y t = new Y(); // new Thread(()->t.m1(),"t1").start(); // new Thread(()->t.m2(),"t2").start(); new Thread(new Runnable() { @Override public void run() { t.m1(); } },"t1").start();; new Thread(new Runnable() { @Override public void run() { t.m2(); } },"t2").start();; } }
可以看到t1先執行,如果不能同時調用那么t2是不能執行的,必須等t1結束,釋放鎖后才能調用,但這里t2確先執行了,所以是可以同時調用的。
2.對業務寫代碼進行加鎖,對讀代碼不進行加鎖,會產生臟讀
package com.dingyu; public class U { private String name; private double banlance; public synchronized void set(String name, double balance) { this.name = name; try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } this.banlance = balance; } public double getBalance() { return banlance; } public static void main(String[] args) { U u = new U(); new Thread(() -> u.set("zhangsan", 500)).start(); System.out.println(u.getBalance()); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(u.getBalance()); } }
3.同線程內一個同步方法可以去調用另一個同步方法(重入鎖 還有一種重入鎖就是子類調用父類的同步方法)
package com.dingyu; public class I { public synchronized void m1() { System.out.println("m1 start"); m2(); System.out.println("m1 end"); } private synchronized void m2() { System.out.println("m2 start"); System.out.println("m2 end"); } public static void main(String[] args) { I i = new I(); new Thread(() -> i.m1()).start(); } }
4.模擬一個簡單的死鎖
package com.dingyu; public class DeadLock { private Object o1 = new Object(); private Object o2 = new Object(); public void m1() { synchronized (o1) { try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2) { System.out.println("如果出現這句話表示沒有死鎖"); } } } public void m2() { synchronized(o2) { synchronized (o1) { System.out.println("如果出現這句話表示沒有死鎖"); } } } public static void main(String[] args) { DeadLock deadLock=new DeadLock(); new Thread(()->deadLock.m1()).start(); new Thread(()->deadLock.m2()).start(); } }
5.如果執行同步方法中出現異常,那么就會自動釋放鎖,如果不想釋放鎖,加上try catch
package com.dingyu; public class ReleaseLock { private int count = 0; private int i = 0; public synchronized void m1() { while (true) { System.out.println(Thread.currentThread().getName() + " " + count++); if (count % 10 == 0) i = 1 / 0; } } public static void main(String[] args) { ReleaseLock releaseLock = new ReleaseLock(); new Thread(() -> releaseLock.m1(), "t1").start(); new Thread(() -> releaseLock.m1(), "t2").start(); } }
6.volatile關鍵字(無鎖同步)
volatile關鍵字 每個線程都有自己的一小塊內存,執行的時候會把變量copy過來,修改了后在寫回對象,
執行m1方法的線程把 running讀到內存里,與此同時主線程也把running讀到內存,並進行修改,寫回對象為false
但是執行m1的線程里的內存一直都是true啊(因為太忙了沒空去刷新)所以會形成死循環,
volatile就是當running改了之后 *立馬去通知其他線程,你們記得去主存刷新一下,一刷新,running為false,退出while循環。
package com.dingyu; /** * volatile關鍵字 每個線程都有自己的一小塊內存,執行的時候會把變量copy過來,修改了后在寫回對象, * 執行m1方法的線程把 running讀到內存里,與此同時主線程也把running讀到內存,並進行修改,寫回對象為false * 但是執行m1的線程里的內存一直都是true啊(因為太忙了沒空去刷新)所以會形成死循環,volatile就是當running改了之后 * 立馬去通知其他線程,你們記得去主存刷新一下,一刷新,running為false,退出while循環。 * @author dingyu * */ public class VolatileDemo { private volatile boolean running = true; public void m1() { System.out.println("m1 start"); while (running) { } System.out.println("m1 end"); } public static void main(String[] args) { VolatileDemo volatileDemo = new VolatileDemo(); new Thread(() -> volatileDemo.m1()).start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } volatileDemo.running = false; } }
7.voliatile 不能保證原子性 不能替換synchronized
package com.dingyu; /** * voliatile 不能保證原子性 不能替換synchronized * * @author dingyu * */ public class VolatileDemo02 { public volatile int count = 0; public void m1() { for (int i = 0; i <= 10000; i++) count++; } public static void main(String[] args) { VolatileDemo02 volatileDemo02 = new VolatileDemo02(); for (int i = 0; i < 3; i++) { new Thread(() -> volatileDemo02.m1()).start(); } try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(volatileDemo02.count); } }
8.多個原子類的方法之間不具備原子性
package com.dingyu; import java.util.concurrent.atomic.AtomicInteger; /** * 原子類 具有原子性,但兩個原子類的方法之間不具備原子性 * * @author dingyu * */ public class AtomicDemo { private AtomicInteger count = new AtomicInteger(); public void m1() { for (int i = 0; i < 100; i++) { count.incrementAndGet(); //兩個原子類的方法之間不具備原子性 count.incrementAndGet(); } } }
9.原子類的不具備可見性
package com.dingyu; import java.util.concurrent.atomic.AtomicBoolean; public class AtomicDemo02 { public AtomicBoolean running = new AtomicBoolean(true); public void m1() { while (running.get()) { } } public static void main(String[] args) { AtomicDemo02 demo02 = new AtomicDemo02(); new Thread(()->demo02.m1()).start(); demo02.running.set(false); } }
10.鎖是鎖在堆內存的那個對象上,而不是引用
package com.dingyu; /** * 鎖是鎖在堆內存的那個對象上,而不是引用 * * @author dingyu * */ public class ChangeReference { public Object o = new Object(); public void m1() { //鎖o synchronized (o) { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } } public static void main(String[] args) { ChangeReference changeReference = new ChangeReference(); new Thread(() -> changeReference.m1(), "t1").start();//啟動一個線程 叫t1 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } changeReference.o = new Object();//引用變了 new Thread(() -> changeReference.m1(),"t2").start();//啟動線程 t2 } }
11.不要鎖字符串常量
package com.dingyu; /** * 不要鎖字符串常量 * @author dingyu * */ public class SynchronizedString { private String s1 = "hello"; private String s2 = "hello"; public void m1() { synchronized (s1) { while(true) {} } } public void m2() { synchronized (s2) { System.out.println("m2 start"); } } public static void main(String[] args) { SynchronizedString synchronizedString = new SynchronizedString(); new Thread(()->synchronizedString.m1()).start(); new Thread(()->synchronizedString.m2()).start(); } }
12.wait 讓線程暫停,釋放鎖, notify 喚醒線程,不釋放鎖
package com.dingyu2; /** * wait 讓線程暫停,釋放鎖, notify 喚醒線程,不釋放鎖 * * @author dingyu * */ public class WaitAndNotyifyDemo { private volatile int count = 0; private Object lock = new Object(); public void m1() { synchronized (lock) { System.out.println("m1 start"); for (int i = 0; i < 10; i++) { count++; System.out.println(count); if (count == 5) { lock.notify(); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } public void m2() { synchronized (lock) { System.out.println("m2 start"); if (count != 5) { try { System.out.println("m2 在等着 但把鎖釋放了"); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("m2 end"); lock.notify(); } } public static void main(String[] args) { WaitAndNotyifyDemo waitAndNotyifyDemo = new WaitAndNotyifyDemo(); new Thread(() -> waitAndNotyifyDemo.m2()).start(); new Thread(() -> waitAndNotyifyDemo.m1()).start(); } }