Java-多线程详解(二)

线程的同步

1.线程同步问题的引出

下面模拟一个简单的卖票程序,两个线程,卖10张票

public class MyClass {
    public static void main(String[] args){
        Ticket ticket1 = new Ticket("线程A");
        Thread t1 = new Thread(ticket1);

        Ticket ticket2 = new Ticket("线程B");
        Thread t2 = new Thread(ticket2);

        t1.start();
        t2.start();
    }
}

//用于卖票的任务
class Ticket implements Runnable{
    //定义所有车票的数量
    public static int num = 10;
    String name;

    public Ticket(String name){
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
                //判断有没有票
                if (num > 0) {
                    System.out.println(name + "出票:" + num--);
                }
            }
        }
    }

运行结果:

线程B出票:10
线程A出票:9
线程B出票:8
线程A出票:7
线程A出票:5
线程A出票:4
线程A出票:3
线程B出票:6
线程B出票:1
线程A出票:2

这时我们发现,操作的结果A、B两个线程余票没有同步。那么,到底是如何造成这种不同步的呢?

卖票的过程分为两个步骤:
第一步:判断是否还有剩余的票数;
第二步:票数减一

2.线程同步问题的解决

(1)同步代码块,通过synchronized关键词解决。同步代码块格式:

synchronized(同步对象){
    需要同步的代码
}

在进行同步的操作时必须设置一个要同步的对象,任意对象我们用Object设置

    class Ticket implements Runnable{
    //定义所有车票的数量
    public static int num = 10;
    String name;

    public Ticket(String name){
        this.name = name;
    }

    Object object = new Object();
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {

            synchronized (object){
                //判断有没有票
                if (num > 0) {
                    System.out.println(name + "出票:" + num--);
                }
            }
        }
    }
}

发现结果还是不正确,这里我们需要主要,任何一个对象都有自己的一把锁,如果多个线程操作同一个代码块,并且需要同步,那么必须操作同一个对象/同一个对象的同一把锁,于是我们

static final Object object = new Object();

这时候我们再运行:

线程A出票:10
线程B出票:9
线程B出票:8
线程B出票:7
线程B出票:6
线程B出票:5
线程B出票:4
线程B出票:3
线程B出票:2
线程B出票:1

运行多次会发现,AB没有交替出票,所以我们要用一个方法使两个线程交替执行

synchronized (object){
            //判断有没有票
            if (num > 0) {
                try {
                    //唤醒同步监听器监听其他线程
                    object.notify();
                    //让当前线程等待
                    object.wait();

                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(name + "出票:" + num--);
            }
        }

这样执行结果:

线程A出票:10
线程B出票:9
线程A出票:8
线程B出票:7
线程A出票:6
线程B出票:5
线程A出票:4
线程B出票:3
线程A出票:2
线程B出票:1

(2)同步方法,使用synchronized关键字将一个方法声明成同步方法。定义格式:

synchronized 方法返回值 方法名称(参数列表){   }

具体如下:

//用于卖票的任务
class Ticket implements Runnable {
    //定义所有车票的数量
    public static int num = 10;
    String name;

    public Ticket(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            this.sale();
        }
    }

    public synchronized void sale(){
        //判断有没有票
        if (num > 0) {
            System.out.println(name + "出票:" + num--);
        }
    }
}

但其实这样是错误的,我们定义的synchronized方法 其实相当于

synchronzied(this){}

但是在执行类里面 我new了两个线程出来,this就不知道到底指哪一个,因为我定义的票数不多,看不出问题,所以建议自己尝试的时候把票数换成100,错误就会很明显了,因为要让程序正确,我们应该这样做

public class MyClass {
    public static void main(String[] args){
        Ticket ticket = new Ticket();

        new Thread(ticket,"线程A").start();
        new Thread(ticket,"线程B").start();
    }
}

 //用于卖票的任务
class Ticket implements Runnable {
    //定义所有车票的数量
    public static int num = 10;

    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            this.sale();
        }
    }

    public synchronized void sale(){
        //判断有没有票
        if (num > 0) {
            System.out.println(Thread.currentThread().getName()+ "出票:" + num--);
        }
    }
}

注意:同步方法必须确保多个对象调用的同步方法是操作的同一个对象

(3)通过锁(Lock)解决

public class MyClass {
    public static void main(String[] args){
        Ticket ticket1 = new Ticket("线程A");
        Thread t1 = new Thread(ticket1);

        Ticket ticket2 = new Ticket("线程B");
        Thread t2 = new Thread(ticket2);

        t1.start();
        t2.start();
    }
}

class Ticket implements Runnable{
    //定义所有车票的数量
    public static int num = 10;
    String name;

    public Ticket(String name){
        this.name = name;
    }

    static ReentrantLock reentrantLock = new ReentrantLock();
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            reentrantLock.lock();
                //判断有没有票
                if (num > 0) {
                    System.out.println(name + "出票:" + num--);
                }
            reentrantLock.unlock();
        }
    }
}

运行结果:

线程A出票:10
线程A出票:9
线程A出票:8
线程B出票:7
线程B出票:6
线程B出票:5
线程B出票:4
线程B出票:3
线程B出票:2
线程B出票:1

你可能感兴趣的:(Java-多线程详解(二))