一、同步
1.作用:为了避免多个线程同时访问并修改共享数据所导致的不安全问题。
2.使用synchronized(对象){}方式。
二、机制
1.当线程要进入某个被“同步锁”锁住代码之前,线程必须先获得“同步锁对象”
2.保证任何时刻,只有一条线程能执行被锁代码
3.不许以“竞争资源”作为“同步对象”
三、同步锁的释放
1.代码完成
2.语句块抛出异常
3.语句块遇到break,return
4.调用wait方法
5.注意:sleep和yield方法不会释放同步锁
四、对象锁池
新建->start()->可运行<---调度任务--> 正在运行->run()完成->终止
正在运行---阻塞事件(sleep,join)----->阻塞-----解除阻塞---->可运行
正在运行---同步的------>阻塞对象的锁池-----获得所---->可运行
五、代码演示
package 同步; public class Couner implements Runnable{ private int counter=200; public void run() { for(int i = 0;i<50;i++){ synchronized (this) { counter-=2; try {Thread.sleep(10);} catch (InterruptedException e) {} System.out.print(counter+" "); } } } }
package 同步; public class TestCounersyc { public static void main(String[] args) { Couner c = new Couner(); Thread t1 = new Thread(c); Thread t2 = new Thread(c); t1.start(); t2.start(); } }
六、死锁
1.定义:两个线程都在等待对方执行完毕才能往下执行时发生死锁,陷入无限等待中。
2.解决:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。
3.死锁代码案例
package 死锁; public class DeadLock { private final Object left = new Object(); private final Object right = new Object(); public void leftRight() throws Exception { synchronized (left) { Thread.sleep(2000); synchronized (right) { System.out.println("leftRight end!"); } } } public void rightLeft() throws Exception { synchronized (right) { Thread.sleep(2000); synchronized (left) { System.out.println("rightLeft end!"); } } } }
package 死锁; public class Thread0 extends Thread { private DeadLock dl; public Thread0(DeadLock dl) { this.dl = dl; } public void run() { try { dl.leftRight(); } catch (Exception e) { e.printStackTrace(); } } }
package 死锁; public class Thread1 extends Thread { private DeadLock dl; public Thread1(DeadLock dl) { this.dl = dl; } public void run() { try { dl.rightLeft(); } catch (Exception e) { e.printStackTrace(); } } }
package 死锁; public class Test { public static void main(String[] args) { DeadLock dl = new DeadLock(); Thread0 t0 = new Thread0(dl); Thread1 t1 = new Thread1(dl); t0.start(); t1.start(); while(true); } }
七、线程交互
1.wait方法使线程进入阻塞状态,直到线程得到唤醒通知,或者达到指定时间。
注意:
1)wait和notify方法仅能在所访问对象的同步块内调用。
2)当线程调用wait方法进入等待状态时,会释放对象锁标记。
2.唤醒方式
通知唤醒-notify或notifyAll方法
主动唤醒-到达阻塞事件
中断唤醒-被另一个线程调用interrupt方法,收到InterruptedException异常。
3.代码演示
package 线程交互; public class Couner implements Runnable{ private int counter=0; public void setCounter(int counter){ this.counter = counter; } public synchronized void run() { if(counter<100){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } counter-=100; System.out.println("counter = " + counter); System.out.println("Counter线程结束"); } }
package 线程交互; public class TestCounerWait { public static void main(String[] args) { Couner c = new Couner(); Thread t = new Thread(c); t.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { } synchronized (c) { c.setCounter(150); c.notify(); } System.out.println("main线程结束"); } }
八、线程综合示例(代码)
package 线程综合示例; public class Couner{ private int counter=0; public synchronized int increase(int n){ counter+=n; this.notify(); System.out.printf("生产:couner=%-2d n=%-2d ",counter,n); return counter; } public synchronized int decrease(int n){ while(counter<n){ try { this.wait(); } catch (InterruptedException e) { } } counter-=n; System.out.printf("消费:couner=%-2d n=%-2d ",counter,n); return counter; } }
package 线程综合示例; public class Producer implements Runnable{ private Couner couner; public Producer(Couner couner){ this.couner = couner; } public void run() { for(int i=0;i<50;i++){ couner.increase((int)(Math.random()*10)); try { Thread.sleep((int)(Math.random()*100)); } catch (InterruptedException e) { e.printStackTrace(); } } } }
package 线程综合示例; public class Consumer implements Runnable{ private Couner couner; public Consumer(Couner couner){ this.couner = couner; } public void run() { for(int i=0;i<50;i++){ couner.decrease((int)(Math.random()*8)); try { Thread.sleep((int)(Math.random()*100)); } catch (InterruptedException e) { e.printStackTrace(); } } } }
package 线程综合示例; public class TestPC { public static void main(String[] args) { Couner couner = new Couner(); Producer p = new Producer(couner); Thread t1 = new Thread(p); Consumer c = new Consumer(couner); Thread t2 = new Thread(c); t1.start(); t2.start(); } }