------- android培训、java培训、期待与您交流! ----------
一、多线程安全问题解析
多线程安全问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
二、解决方法
解决办法:对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
java对于多线程的安全问题提供了专业的解决方式。
synchronized(对象)
{
需要被同步的代码
}
对象如同锁。只有锁的线程可以在同步中执行。
没有持有锁的线程即使获取了cpu的执行权,也进不去。
如同商场中的试衣间,一个人出去,另外一个进来。不过也有例外就是优衣库的试衣间,这个不算。
同步的前提:
1,必须要有两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。
3,必须保证同步中只能有一个线程在运行。
如果加了同步,还出现错误。说明没有达到前提。
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
同步代码块例:
{
private int tick = 100;
Object obj = new Object();//这里可以是任意对象。
public void run()
{
while(true)
{
synchronized(obj)
{
if (tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}//这里加try是因为sleep方法默认的抛出了错误。所以必须处理
System.out.println(Thread.currentThread().getName()+"...sale:"+tick--);
}
}
}
}
}
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();
}
}
如何找同步中的问题:
1,明确哪些代码是多线程运行代码
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
另外synchronized可作为修饰符修饰函数使函数具备相同功能。publi synchronized .........
并且同步函数的锁是this.
证明同步函数锁是this:
通过该程序进行验证。使用两个线程来买票。一个线程在同步代码块中。
一个线程在同步函数中。都在执行买票动作。
class Ticket implements Runnable
{
private int tick = 100;
Object obj = new Object();//这里new对象是因为下面的同步锁要传入一个对象,任意对象都可以
boolean flag = true;//但是如果传别的对象,还需要在建一个类。
//public synchronized void run()//不可以这么做。因为其中有些代码不需要同步。
/*public void run()
{
while (true)
{
show(); //相当于this.show();
}
}*/
public void run()//为了看两个同步是否为同一个锁。一个线程执行同步代码块。一个线程执行同步函数。
{
if (flag)
{
while(true)
{
synchronized(this) //证明了同步函数的锁为this,因为如果这里用obj锁的话,会出现错误。只有多进程使用一个锁的时候结果才是正确的。
{
if (tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....sale:"+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){}//如果这里t1线程不Sleep那么主线程就会把后面的都执行。
t.flag = false;
t2.start();
}
}
如果同步函数时静态的话,那么它的锁是Class.
因为静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
类名.Class 该对象的类型是Class
其实懒汉式的单例模式很好的说明了同步的作用
class Single
{
private Static Single s = null;
private Single(){};
public static Single getIntance()
{
if(s==null) //这样做能提高效率,不用每个都判断锁。
{
synchronized(Single.class) //因为是静态同步方法锁是class
{
if(s==null)
s = new Single;
}
}
return s;
}
}
三、死锁问题
死锁:同步中嵌套同步,而锁却不同就会造成死锁。程序进入等待
class Ticket implements Runnable
{
private int tick = 100;
Object obj = new Object();
boolean flag = true;
public void run()
{
if (flag)
{
while(true)
{
synchronized(obj)
{
show();
}
}
}
else
while(true)
show();
}
public synchronized void show() //这里锁是this与另外的同步代码块锁不同,造成死锁。
{
synchronized(obj)
{
if (tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....show...:"+tick--);
}
}
}
}
class DeadLockDemo
{
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();
}
}