线程安全问题
线程安全问题产生的原因:多条线程代码操作共享数据,一条线程还未执行完,并对结果进行反馈;其他的线程通过条件判断参与进运算,对数据重复操作
多次启动一个线程是非法的,尤其是线程已经结束后,不能重新启动
买票示例
/*卖票示例
*/
class Ticket implements Runnable
{
private int num = 100;
public void sale()
{
while (true)//
{
if (num>0)
{
try//使用sleep方法时会出现异常,继承的接口没有继承Exception 异常,所以run方法不能throws,只能try catch
{
Thread.sleep(10);//线程休眠10毫秒
}
catch (InterruptedException e)
{
}
System.out.println("name: "+Thread.currentThread().getName()+"....."+num--);
}
}
}
public void run()
{
sale();
}
}
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();
}
}
运行结果出现负数票
解决方法:
当前线程执行完毕后,其它线程才能参与运算。
将共享数据的代码封装起来,当有线程在执行这些代码的时候。其它线程不可以参与运算,使用
线程同步
class Ticket implements Runnable
{
private int num = 100;
Object obj =new Object();
public synchronized void sale()//同步函数,不能直接调用,因为一个线程进入后全部执行完才出来,其它线程没有锁所以执行不到
{
while (true)
{
//synchronized(obj)//同步代码块,同步锁;需要传入对象,可以是任意对象,作为自定义锁
//{
if (num>0)
{
try
{
Thread.sleep(10);//线程休眠10毫秒
}
catch (InterruptedException e)
{
}
System.out.println("name: "+Thread.currentThread().getName()+"....."+num--);
}
//}
}
}
public void run()//所有线程都可以调用run方法,在run方法中间接调用同步函数,从而间接执行到sale();
{
sale();
}
}
对象如同锁,同步就相当于一个保险箱,持有锁的线程可以进入同步中执行,没有锁的线程即使获取cpu执行权,也无法进去执行
经典实例----火车上的卫生间
同步的前提:多个线程使用同一个锁,必须保证同步中只有一个线程在执行
同步的弊端:多个线程需要判断锁,较为消耗资源
同步的原则:明确哪些代码是多线程运行代码;明确共享数据;明确多线程运行代码哪些是操作共享数据的,只能有一个共同的锁
同步的方式
①同步代码块
synchronized(对象){需要被同步的代码}//只需要把需要同步的代码封装在同步中,同步代码块的锁是传入的对象,可以是任意对象,自定义锁。
②同步函数
同步函数的锁:函数需要被对象调用,那么函数都有一个所属对象的引用,就是this。即当前任务对象
(考点)静态同步函数使用的锁是该方法所在类的字节码文件对象,类名.class
静态成员进内存的过程:内存中没有本类对象,但是一定有该类对应的字节码文件对象,类名.class,该对象的类型是class
总结:同步函数中,那个对象调用了同步,同步锁就是该对象的引用
单例模式的线程安全问题
懒汉式特点:对象延迟加载,多线程访问时存在安全隐患;可以加同步来解决。同步过程中判断锁会降低效率,而同步函数每一条线程访问都会有判断锁的过程;使用同步代码块二次判断,用判断变量来替代判断锁,只需判断一次锁,稍微提高了执行效率
由于是静态同步,所以懒汉式中的同步锁是本类对象的字节码文件//饿汉式
class Single
{
private static final Single s=new Single();//有final修饰
private Single(){}
public static Single getInstance()
{
return s;
}
}
//懒汉式
class Single
{
private static Single s =null;//无final修饰
private Single(){}
//public static Single getInstance()//延迟加载,多个线程并发访问时可能引发安全问题
//public static synchronized Single getInstance()使用同步函数,多了一步判断锁的过程,多线程时较为低效
public static Single getInstance()//使用同步代码块
{
if (s==null)//减少判断锁的次数,稍微提高效率
{
synchronized(Single.class)//静态同步的锁
{
if(s==null)
s=new Single();
}
return s;
}
}
}
线程死锁
死锁的条件:同步中嵌套同步,同步之间的调用,由于每个同步只有一个对应的锁,相互嵌套时同时持有两个锁才会执行,一旦两个同步都持有一个对方的锁无法执行也无法释放,就会导致线程死锁。这种情况应避免发生
死锁示例:
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if (flag)
{
synchronized(MyLock.a)
{
System.out.println("if.....a");
synchronized(MyLock.b)
{
System.out.println("if....b");
}
}
}
else
{
synchronized(MyLock.b)
{
System.out.println("else....b");
synchronized(MyLock.a)
{
System.out.println("else......a");
}
}
}
}
}
class MyLock
{
static Object a= new Object();
static Object b= new Object();
}
class DeadLock
{
public static void main(String[] args)
{
Thread t1=new Thread(new Test(true));
Thread t2=new Thread(new Test(false));
t1.start();
t2.start();
}
}