版权声明:未经同意,严禁转载 https://blog.csdn.net/pengchengliu/article/details/83213120
点击此处返回总目录 一、线程的安全问题
多个线程同时操作一个共享数据,可能会出现安全问题。比如下面的程序: //Ticket.java
package cn.itcast.demo01; public class Ticket implements Runnable { private int tickets = 3; public void run(){ while(true){ if(tickets>0){ //1处。 try { Thread.sleep(10); //首先休眠10毫秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"sold:"+tickets--); //然后再卖一张票 } } } } |
//Test.java
package cn.itcast.demo01; public class Test { public static void main(String[] args) { Ticket ti = new Ticket(); //创建了一个对象。成员变量tickets只有一份。下面的多个run()方法走的是同一个变量。 Thread th0 = new Thread(ti); Thread th1 = new Thread(ti); Thread th2 = new Thread(ti); th0.start();th1.start();th2.start(); } } |
运行结果: Thread-2sold:3 Thread-0sold:2 Thread-2sold:1 Thread-1sold:0 Thread-0sold:-1 结果分析: 按照尝试来讲,卖出3,2,1之后就应该停止了。但是程序还多卖了两张。这就是线程不安全问题。 当线程1运行到1处时,发现还有1张票。然后就准备卖,但是还没卖。这时候线程2进来了,发现还是有1张票。于是就卖了,票数就变成了0。当线程2运行完之后,线程1继续执行,结果就导致变成了-1。 二、同步代码块的方式,解决线程安全问题 为了解决安全问题,java提供了同步技术,保证安全性。 公式: synchronized(同步锁){ 线程要操作的共享代码段 } 同步代码块的原理: 同步锁可以是任意的对象,专业词汇叫对象监视器。 没有锁的线程不能执行,只有等待。先获取锁,然后执行,最后释放锁。 使用: //Ticket.java
package cn.itcast.demo01; public class Ticket implements Runnable { private int tickets = 30; private Object obj = new Object(); public void run(){ while(true){ synchronized (obj) { //包起来的是共享代码段。这里用的是自己在类中定义一个对象,然后作为同步锁 的方式。也可以不用定义对象,直接写"this",this就是本类对象的引用。就是后面创建 的对象"ti"。 if(tickets>0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"sold:"+tickets--); } }//synchronized } } } |
//Test.java
package cn.itcast.demo01; public class Test { public static void main(String[] args) { Ticket ti = new Ticket(); Thread th0 = new Thread(ti); Thread th1 = new Thread(ti); Thread th2 = new Thread(ti); th0.start();th1.start();th2.start(); } } |
运行结果,可以看到没有线程安全问题了。同时可以看到运行慢了,也就是说,线程安全意味着程序运行慢。 三、采用同步方法的方式,解决线程安全问题 同步方法的好处:代码量比较简洁 将线程共享数据,抽取到一个方法中。在方法的声明上加入同步关键字synchronized。这种方法称为同步方法。 同步方法中就不需要再使用syschronized(mutex){}同步代码块了。 问题:同步方法中还有同步锁么? 有。方法中的同步锁就是本类对象的引用this。 如果同步方法是静态方法,也是有同步锁的。但是因为静态方法与对象无关,所以他的同步锁肯定不是this。静态同步方法的同步锁是本类类名.class属性。(先记住即可,原因与类加载器和反射有关系) 使用: //Ticket.java
package cn.itcast.demo01; public class Ticket implements Runnable { private int tickets = 3; public void run(){ while(true){ sell(); } } public synchronized void sell(){ //同步方法 if(tickets>0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"sold:"+tickets--); } } } |
Test.java
package cn.itcast.demo01; public class Test { public static void main(String[] args) { Ticket ti = new Ticket(); Thread th0 = new Thread(ti); Thread th1 = new Thread(ti); Thread th2 = new Thread(ti); th0.start();th1.start();th2.start(); } } |
三、Lock接口的方式,解决线程安全问题 同步方法 |
|