黑马程序员 JAVASE——多线程安全(并发问题和死锁等)

来源:互联网 发布:caffe安装教程ubuntu 编辑:程序博客网 时间:2024/06/03 17:10

----------- android培训java培训java学习型技术博客、期待与您交流! ------------

Ps:部分内容为黑马毕向东老师视频整理,非完全原创。Jdk1.5以后线程新特性会在以后的博文中详细讲述。敬请期待



:1多线程的安全问题当多线程在使用共享数据时,就有可能出现安全问题。

     2代码下面通过代码来说明这个问题:

package lq.java.fuxi.day5;
class Ticket implements Runnable//extends Thread
{
private int tick = 100;
public void run()
{
while(true)
{
if(tick>0)
{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread

().getName()+"....sale : "+ tick--);
}
}
}
}


public class TicketDemo
{
public static void main(String[] args)
{

Ticket t = new Ticket();

Thread t1 = new Thread(t);//创建了一个线程;
Thread t2 = new Thread(t);//创建了一个线程;
Thread t3 = new Thread(t);//创建了一个线程;
Thread t4 = new Thread(t);//创建了一个线程;
t1.start();
t2.start();
t3.start();
t4.start();

}
}


运行结果出现了:
。。。 。。。
Thread-3....sale : 0
Thread-0....sale : -1
Thread-1....sale : -2

3分析通过分析,发现,打印出0-1-2等错票。

多线程的运行出现了安全问题。

问题的原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。

而把代码中的的一下代码注释掉,则不会发生安全问题

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

此时线程中的执行语句就一句话:System.out.println(Thread.currentThread

 

这样一来线程就不会在执行的半路上被打断。就是安全的

 

4总结:对于多线程的安全问题,知道了他的发生原因后,我们就要解决它。

解决办法:

对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。

Java对于多线程的安全问题提供了专业的解决方式。下面我们详细介绍:


二:解决线程的安全问题机制

1同步代码块:

I:格式:

synchronized(对象)

{

需要被同步的代码

 

}

对象如同锁。持有锁的线程可以在同步中执行。

形象的例子巧记:火车上的卫生间。

II:代码:

class Ticket implements Runnable

{

private  int tick = 1000;

Object obj = new Object();

public void run()

{

while(true)

{

synchronized(obj)

{

if(tick>0)

{

//try{Thread.sleep(10);}catch(Exception e){}

System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);

}

}

}

}

}

 

public class  TicketDemo2

{

public static void main(String[] args) 

{

Ticket t = new Ticket();

 

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

Thread t3 = new Thread(t);

Thread t4 = new Thread(t);

t1.start();

t2.start();

t3.start();

t4.start();

}

}

运行结果就不会有问题了

2同步函数:

I:格式:在函数的声明部分加上synchronized关键字即可。加载访问修饰符和返回值之间。

II:问题:同步函数为了同步代码块的书写简化,但是这样一来:同步函数使用了那个对象来作为锁呢?

猜想:函数需要被对象调用。那么函数都有一个所属对象引用。就是this

所以同步函数使用的锁是this

 

验证:通过该程序进行验证。

 

使用两个线程来买票。

一个线程在同步代码块中。

一个线程在同步函数中。

都在执行买票动作。

class Ticket implements Runnable
{
private int tick = 100;
Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(this)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
}
}
}
}
else
while(true)
show();
}
public synchronized void show()//this
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
}
}
}


class ThisLockDemo
{
public static void main(String[] args)
{

Ticket t = new Ticket();

Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
t2.start();

}
}
分析:运行结果没有出现错误,所以验证了同步函数使用的锁时this。


3:静态同步函数:

I:问题引入:

如果同步函数被静态修饰后,使用的锁是什么呢?

 

不在是this。因为静态方法中也不可以定义this

 

静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。

类名.class  该对象的类型是Class

 

静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class

II:代码验证

class Ticket implements Runnable

{

private static  int tick = 100;

//Object obj = new Object();

boolean flag = true;

public  void run()

{

if(flag)

{

while(true)

{

synchronized(Ticket.class)

{

if(tick>0)

{

try{Thread.sleep(10);}catch(Exception e){}

System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);

}

}

}

}

else

while(true)

show();

}

public static synchronized void show()

{

if(tick>0)

{

try{Thread.sleep(10);}catch(Exception e){}

System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);

}

}

}

 

 

class  StaticMethodDemo

{

public static void main(String[] args) 

{

 

Ticket t = new Ticket();

 

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

t1.start();

try{Thread.sleep(10);}catch(Exception e){}

t.flag = false;

t2.start();

}

}

二:死锁

I:概念:死锁也是线程的一个安全隐患,即同步中嵌套同步,

操作系统中的死锁被定义为系统中两个或者多个进程无限期地等待永远不会发生的条件,系统处于停滞状态,这就是死锁。

例如:两个人要吃饭,但是他们手里只有一只筷子,所以每个人都想要对方的筷子,这样一来,程序就会死锁。但也有和谐的时候,即你把筷子给我我吃俩口,然后再给你,这样交替执行。

II:代码说明:

class Test implements Runnable

{

private boolean flag;

Test(boolean flag)

{

this.flag = flag;

}

 

public void run()

{

if(flag)

{

while(true)

{

synchronized(MyLock.locka)

{

System.out.println(Thread.currentThread().getName()+"...if locka ");

synchronized(MyLock.lockb)

{

System.out.println(Thread.currentThread().getName()+"..if lockb");

}

}

}

}

else

{

while(true)

{

synchronized(MyLock.lockb)

{

System.out.println(Thread.currentThread().getName()+"..else lockb");

synchronized(MyLock.locka)

{

System.out.println(Thread.currentThread().getName()+".....else locka");

}

}

}

}

}

}

 

 

class MyLock

{

static Object locka = new Object();

static Object lockb = new Object();

}

 

public class  DeadLockTest

{

public static void main(String[] args) 

{

Thread t1 = new Thread(new Test(true));

Thread t2 = new Thread(new Test(false));

t1.start();

t2.start();

}

}

运行结果:

Thread-0...if locka 

Thread-1..else lockb

分析:在本例中就属于同步中潜逃同步,所以会发生死锁。

死锁是没用的,但是明白死锁的原理后,以后就要在程序的开发过程中尽量避免死锁。

一下是在操作系统中死锁的内容,稍微理解就行。

 死锁的必要条件:

  (1) 互斥条件:一个资源每次只能被一个进程使用。                                                                      
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。                                                   
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。                                                
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之
一不满足,就不会发生死锁。 死锁的解除与预防:



---- ASP.Net+Android+IOS开发.Net培训、期待与您交流!--------详细请查看:http://edu.csdn.net------


原创粉丝点击