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

原创 2013年12月02日 10:33:34

----------- 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------


并发程序中如何规避死锁

通常意义上的死锁是由于不同线程(也可能是进程或者虚拟线程)请求锁的顺序不同造成的。google如何避免死锁,随便一篇文章都会告诉你只要按照同样的顺序请求锁,就可以避免死锁。这可能适用于大部分并发应用。...
  • runyon1982
  • runyon1982
  • 2015年10月10日 09:19
  • 1213

并发多线程之死锁-----哲学家吃饭问题

该例子说明了4点出现死锁需要同时满足的条件: 互斥条件:任务使用的资源至少有一个是不能共享的。这里,一根chopstick(筷子)一次就只能让一个philosopher(哲学家)使用。 至少有一个任务...
  • linfujian1999
  • linfujian1999
  • 2017年01月15日 21:29
  • 548

redis分布式锁处理并发问题

redis锁处理并发问题redis锁处理高并发问题十分常见,使用的时候常见有几种错误,和对应的解决办法,在此进行自己的总结和整理。 set方式 setnx方式 setnx+getset方式 set方式...
  • d1562901685
  • d1562901685
  • 2017年02月05日 16:31
  • 5179

java挑战高并发(9):死锁

当线程需要同时持有多个锁时,有可能产生死锁。考虑如下情形:       线程A当前持有互斥所锁lock1,线程B当前持有互斥锁lock2。接下来,当线程A仍然持有lock1时,它试图获取lock2,...
  • A1023824314
  • A1023824314
  • 2016年08月03日 17:00
  • 1397

【java并发】线程技术之死锁问题

我们知道,使用synchronized关键字可以有效的解决线程同步问题,但是如果不恰当的使用synchronized关键字的话也会出问题,即我们所说的死锁。死锁是这样一种情形:多个线程同时被阻塞,它们...
  • eson_15
  • eson_15
  • 2016年05月29日 09:52
  • 4359

使用apache log解决高并发下log4j引起大量线程block问题

由于项目用户量比较大,测试同事采用两百并发进行测试,在测试查过程中,查看jvm 虚拟机发现很多java.lang.Thread.State: BLOCKED (on object monitor) ...
  • Mr_Smile2014
  • Mr_Smile2014
  • 2016年09月01日 17:21
  • 8115

MySQL并发引起的死锁问题

PHP爱好者 2017-04-27 15:14 背景: 平台的某个数据库上面有近千个连接,每个连接对应一个爬虫,爬虫将爬来的数据放到cdb里供后期分析查询使用。前段时间经常出现cdb查询缓慢,c...
  • u011277123
  • u011277123
  • 2017年04月28日 11:14
  • 534

JAVA多线程不安全问题解决方案(多线程并发同一资源)。

引例:吃苹果比赛,3个人同时吃50个苹果,谁先拿到谁就吃,每个哦ing过都有编号。 问题: 多线程同时执行的时候可能出现不安全问题 当3个人同时拿到一个苹果,他们的编号就一样,当时主要看是谁先吃掉苹果...
  • blank__box
  • blank__box
  • 2017年04月24日 10:28
  • 1225

常见事务并发问题以及处理方法

1、数据库事务并发会引起那些常见问题以及对应的解决方法? 1)丢失更新:撤消一个事务时,把其它事务已提交的更新的数据覆盖了。 2)脏读:一个事务读到另一个事务未提交的更新数据。 3)幻读:一...
  • songwei128
  • songwei128
  • 2015年02月05日 21:40
  • 2360

一个数据库死锁问题

一【场景】 之前系统在运行过程中,老是报一个诡异的死锁检测异常: Error Code: 1213 Deadlock found when trying to get lock; try ...
  • icenic
  • icenic
  • 2017年10月09日 04:07
  • 127
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:黑马程序员 JAVASE——多线程安全(并发问题和死锁等)
举报原因:
原因补充:

(最多只允许输入30个字)