多线程(2)----线程的同步互斥

版权声明:未经同意,严禁转载 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接口的方式,解决线程安全问题

同步方法

 

猜你喜欢

转载自blog.csdn.net/pengchengliu/article/details/83213120