编程语言
首页 > 编程语言> > 线程同步(JAVA笔记-线程基础篇)

线程同步(JAVA笔记-线程基础篇)

作者:互联网

在多线程应用程序中经常会遇到线程同步的问题。比如:两个线程A线程B可能会 “同时” 执行同一段代码,或修改同一个变量。而很多时候我们是不希望这样的。
这时候,就需要用到线程同步。

多线程引发的问题

为了演示多线程引发问题,我们模仿买票,写一个简单的小程序。

上面的例子模拟了多窗口买票,但是看,输出结果是不是又问题?怎么还有第(-1)张票。难道还有站票不成?当然不存在的,这是我们程序出现了问题。
这就是多线程同时操作统一参数的问题。也就是上面例子中SellTicket对象的private int tickets=30;属性。

p.s.我运行了好多遍都没出现错误的情况,后面给每个方法休眠了0.5秒才出现上面的错误结果。说明多线程不同步代码发生的错误不是百分之百的,只是有一定的概率。

为什么会出现上面这种情况?

众所周知,多线程的同步不是真的同步执行的。只是CPU切换运行线程所以看上去是几个线程同步执行。理解了这个概念往下看。(实在不懂的可以百度一下其他大佬的文章,我之后可能还会写一个笔记来记录-如果有必要的话)

Java线程同步

为了避免上面的情况,可以使用以下三个方法来解决。

  1. 同步代码块
  2. 同步方法
  3. 同步锁

同步代码块

直接上代码

synchronized (this){
    //这里的代码,只允许一个线程运行。
    //等一个线程运行结束,把锁交给下一个等待的线程运行。
}

说明
1.this: 这里的this是同步锁也叫同步监听对象。
2.this可以是任何对象。
3.但是一般把当前多线程,并发访问的共同资源当作同步锁。在例子中也就是sellTicket对象。所以在对象里面可以写程this

修改上面的例子

public void run() {
    //写一个死循环,模拟在不断的卖票。
    while (true){
        //票数大于零,代表还有票,继续卖。
        //如果票数小于等于零,也就是没票了。跳出循环,停止卖票
        synchronized (this.getClass()){
            if(tickets > 0){
                sell();
            }else{
                break;
            }
        }
    }
}

同步方法

同步方法,其实跟同步代码块差不多,不过这个是在方法上添加synchronized关键字。

public synchronized void sell(){
    //这里的代码,只允许一个线程运行。
    //等一个线程运行结束,把锁交给下一个等待的线程运行。
}

要在上面的例子中使用同步方法,需要改一下。把if()判断放到sell()方法里面。

public synchronized void sell(){
    //在sell()方法在判断下当前票数
    if(tickets > 0){
        //记录下现在的票数
        int oldNumber = tickets;
        //卖掉一张后的票数,--tickets代表票数减一,模拟卖掉了一张票
        int nowNumber = --tickets;
        System.out.println(Thread.currentThread().getName()+",卖出了第("+ oldNumber +")张票,还剩("+ nowNumber +")张票。");
    }
}

同步锁

以上两种方法,都需要一个关键字synchronized。同步锁需要用到一个接口。

public interface Lock {
    //生源n多代码,详细的去看源码。
}

接口里面有两个比较重要的方法。

/**
* 请求一个锁
* Acquires the lock. 
*/
void lock();
/**
* 释放锁
* Releases the lock.
*/
void unlock();

在这两个方法之间的代码都是同步的。
但是Lock只是个接口,没法使用。
JUC包给我们一个常用的实现类ReentrantLock。部分源码如下:

public class ReentrantLock implements Lock, java.io.Serializable {
    //感兴趣的可以去看看源码
}

修改我们的案例代码

public class SellTicket implements Runnable {
    //有30张票
    private int tickets=30;

    Lock lock=new ReentrantLock();

    public void run() {
        //写一个死循环,模拟在不断的卖票。
        while (true){
            //票数大于零,代表还有票,继续卖。
            //如果票数小于等于零,也就是没票了。跳出循环,停止卖票
            lock.lock();
            //synchronized (this.getClass()){
            if(tickets > 0){
                sell();
            }else{
                break;
            }
            //}
            lock.unlock();

        }
    }
    //买票方法,模拟买票的动作
    public synchronized void sell(){
            //记录下现在的票数
            int oldNumber = tickets;
            //卖掉一张后的票数,--tickets代表票数减一,模拟卖掉了一张票
            int nowNumber = --tickets;
            System.out.println(Thread.currentThread().getName()+",卖出了第("+ oldNumber +")张票,还剩("+ nowNumber +")张票。");
    }
}

瞎总结

标签:tickets,同步,JAVA,张票,笔记,线程,窗口,票数
来源: https://www.cnblogs.com/Eastry/p/13081157.html