标签:
------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 实现组合使用。
标签:
原文地址:http://www.cnblogs.com/tozr/p/4150570.html