------Java培训、Android培训、iOS培训、期待与您交流! -------
安全问题产生的原因:
当多条代码语句在操作同一个共享数据时,一个线程对多条语句只执行了一部分,还没有执行完, 另一个线程参与进来执行。导致共享数据的错误。
class Mlpc implements Runnable { private int ticket = 50; public void run() { while(true) { if(ticket>0) { try { Thread.sleep(20); //让线程进入冻结状态 } catch (Exception e) { } System.out.println(Thread.currentThread()+" "+ticket--); } } } } class mlpcDemo { public static void main(String[] args) { Mlpc m = new Mlpc(); Thread th0 = new Thread(m); Thread th1 = new Thread(m); Thread th2 = new Thread(m); Thread th3 = new Thread(m); th0.start(); th1.start(); th2.start(); th3.start(); } }出现了票数为-1和-2这样的结果。
解决办法:
针对多条线程将会操作共享数据的那条语句,每次只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
方式:
同步代码块:
class Mlpc implements Runnable { private int ticket = 50; Object obj = new Object(); public void run() { while(true) { synchronized (obj) { if(ticket>0) { try { Thread.sleep(20); } catch (Exception e) { } System.out.println(Thread.currentThread()+" "+ticket--); } } } } }对象如同锁,持有锁的线程可以在同步中执行。非持有锁的线程,即使获取cpu的执行权,也进不去,因为没有获取锁。
同步的前提:
1.必须要有两个或者两个以上的线程
2.必须多个线程必须使用同一个锁。 必须保证同步中只能有一个线程在运行。
好处:解决了线程的安全问题
弊端:消耗了资源,多个线程需要判断锁。
两个储户分别向银行存入300元,每次100,存3次。
找出改程序的安全问题。
1,明确哪些代码是多线程运行代码。
2,明确共享数据
3,明确多线程运行代码中哪些语句是操作共享数据的。
我们创建4个线程,与之前不同的是,同时使用了同步函数和同步代码快。看看结果如何。
class Mlpc implements Runnable { private int ticket = 550; public boolean flag = true; Object obj = new Object(); public void run() { if(flag == true) { while(true) { synchronized (obj) { if(ticket>0) { try { Thread.sleep(20); } catch (Exception e) { } System.out.println(Thread.currentThread()+" show "+ticket--); } } } } else { while(true) { show(); } } } public synchronized void show() { if(ticket>0) { try { Thread.sleep(20); } catch (Exception e) { } System.out.println(Thread.currentThread()+" show "+ticket--); } } } class mlpcDemo { public static void main(String[] args) { Mlpc m = new Mlpc(); Thread th0 = new Thread(m); Thread th1 = new Thread(m); Thread th2 = new Thread(m); Thread th3 = new Thread(m); th0.start(); try { Thread.sleep(20); } catch (Exception e) { } m.flag = false; th1.start(); th2.start(); th3.start(); } }结果出现了票数为0。
public void run() { if(flag == true) { while(true) { synchronized (this) //从原本的任意对象改为函数自己所属的对象this { if(ticket>0) { try { Thread.sleep(20); } catch (Exception e) { } System.out.println(Thread.currentThread()+" show "+ticket--); } } } } else { while(true) { show(); } } }现在,无论如何都显示正常结果。由此可以验证出函数都有自己所属的对象this,所以同步函数所使用的是this锁。
死锁:
当类中存在多个不同的锁时,而这些锁存在交叉,那么就可能会出现死锁
如:
1,锁A--11
{锁B--22}
2,锁B--33
{锁A--44}
像上面这样出现交叉的锁。如11 33 同时执行后 要运行22 44就锁住了。22被下面B锁了,44又被上面11锁住了。那么就执行不下去了。
class Demo implements Runnable { private boolean flag; Demo(boolean flag) { this.flag = flag; } public void run() { if(flag) { while(true) synchronized(MyLock.locka) { System.out.println("if locka"); synchronized(MyLock.lockb) { System.out.println("if lockb"); } } } else { while (true) synchronized(MyLock.lockb) { System.out.println("else lockb"); synchronized(MyLock.locka) { System.out.println("else locka"); } } } } } class MyLock { static Object locka = new Object(); static Object lockb = new Object(); } class ThreadTest { public static void main(String[] args) { Thread t1 = new Thread(new Demo(true)); Thread t2 = new Thread(new Demo(false)); t1.start(); try { Thread.sleep(100); } catch (Exception e) { } t2.start(); } }
生产者与消费者问题:
1 class PCDemo 2 { 3 public static void main(String[] args) 4 { 5 Resource r = new Resource(); 6 Producer prd = new Producer(r); 7 Consumer csu = new Consumer(r); 8 9 Thread th0 = new Thread(prd); 10 Thread th1 = new Thread(prd); 11 Thread th2 = new Thread(prd); 12 Thread th3 = new Thread(prd); 13 14 Thread th4 = new Thread(csu); 15 Thread th5 = new Thread(csu); 16 Thread th6 = new Thread(csu); 17 Thread th7 = new Thread(csu); 18 th0.start(); 19 th1.start(); 20 th2.start(); 21 th3.start(); 22 th4.start(); 23 th5.start(); 24 th6.start(); 25 th7.start(); 26 } 27 } 28 29 30 class Resource 31 { 32 private String name; 33 private int count =1; 34 private boolean flag = false; 35 36 public synchronized void set(String name) 37 { 38 if(flag) 39 try 40 { 41 wait(); 42 } 43 catch (Exception e) 44 { 45 } 46 this.name = name+"---"+count++; 47 System.out.println(Thread.currentThread().getName()+"生產"+this.name); 48 flag = true; 49 this.notify(); 50 } 51 public synchronized void out() 52 { 53 if(!flag) 54 try 55 { 56 wait(); 57 } 58 catch (Exception e) 59 { 60 } 61 System.out.println(Thread.currentThread().getName()+"________消費"+this.name); 62 flag = false; 63 this.notify(); 64 } 65 66 } 67 68 class Producer implements Runnable 69 { 70 private Resource res; 71 Producer(Resource res) 72 { 73 this.res = res; 74 } 75 public void run() 76 { 77 while(true) 78 { 79 res.set("物件"); 80 } 81 } 82 } 83 84 class Consumer implements Runnable 85 { 86 private Resource res; 87 Consumer(Resource res) 88 { 89 this.res = res; 90 } 91 public void run() 92 { 93 while(true) 94 { 95 res.out(); 96 } 97 } 98 }如果只是开启了两个线程,运行时会轮流“消费--生产”很和谐。但如果两者各开启两条线程,就会出现这样的情况。
t1取得资格、执行权->生产商品1、将flag之位true后t1进入阻塞状态、t2取得资格和执行权->进入阻塞状态,t3取得资格和执行权->消费商品1->唤醒第一个进入线程池的t1、t3而后阻塞、t1取得资格,若同样取得资格t4抢到执行权->t4阻塞->t1取得执行权->生产商品2->唤醒t2、t2取得执行权后不再判断flag真假直接生产t3。
于是将if判断改为while循环,这样的确弥补了错误。但会导致t2"醒来"->判断flag真假->t2进入阻塞状态。造成了所有线程全部等待。于是使用notifyAll();总是唤醒全部线程。又一次解决了问题。
JDK1.5提供了多线程升级解决方案,将同步synchronized替换成现实lock操作,要为特定 Lock 实例获得 Condition 实例,使用其 newCondition() 方法。
Condition 实例实质上被绑定到一个锁上。Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,
以便通过将这些对象与任意 Lock 实现组合使用。
转载于:https://www.cnblogs.com/tozr/p/4150570.html
