[java多线程]的同步问题

指的是多个线程同时修改一个过程时,可能导致的问题

引入:

以卖票问题为例:假设总共有十张票,三个黄牛卖票(代表三个子线程),预测最后一个黄牛卖完票,还剩下0张票

package se.SE.practice;
class MyThread implements Runnable {
    private int ticket=10;
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (this.ticket > 0) {
                try {
                    // 模拟网络延迟
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "还剩下:" + this.ticket-- + "票");
            }
        }
    }
}
public class Thread1{
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        Thread thread1=new Thread(mt,"黄牛1");
        Thread thread2=new Thread(mt,"黄牛2");
        Thread thread3=new Thread(mt,"黄牛3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

执行结果:

我们发现最后一个黄牛卖票还剩下1张票,和预期不符

为什么会出现这个问题呢?是因为在多线程并发执行的时候,发生了同步的问题,也就是多个线程同时访问同一块资源(ticket) 

解决思路:

在黄牛线程访问ticket时,不允许有其他线程访问ticket

引入:synchronized关键字

Object someObject =new Object();
synchronized (someObject){
  //此处的代码只有占有了someObject后才可以执行
}

卖完票才解锁:在for循环后加synchronized(this){}

也就是在任何一个时刻内只有一个线程在卖票,

三个线程(黄牛1,黄牛2,黄牛3):可以同时进入for循环和run()但是不可同时进入synchroniazed锁住的代码块

 public void run() {
        for (int i = 0; i < 100; i++) {

            synchronized (this) {
//---------------------------
                if (this.ticket > 0) {
                    try {
                        // 模拟网络延迟
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "还剩下:" + this.ticket-- + "票");
                }
            }
//-------------------------------------------------
        }
    }

1同步问题:

a.同步代码块

在方法中使用synchronized对象,一般可以锁定当前this

表示同一时刻只有一个线程可以进入同步代码块,但是多个线程可以同时进入方法

b.同步方法

在方法声明中加入synchronized,表示此时只有一个线程可以进入方法

2synchronized对象锁

观察以下代码:

1定义一个synchronized方法,锁住当前对象this

2第一次new了一个Syn对象syn1,记线程0,那么线程0

线程1启动创建Syn对象syn2,各自锁各自的对象

package se.SE.practice;
class Syn{
    public synchronized void test(int ticket){
        System.out.println(Thread.currentThread().getName()+"此方法开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"此方法结束");
    }
}
class MyThread implements Runnable {
    private int ticket = 10;
    @Override
    public void run() {
        Syn syn=new Syn();
        syn.test(ticket);
    }
}
public class Thread1{
    public static void main(String[] args) {
                   MyThread myThread=new MyThread();
            Thread thread0=new Thread(myThread,"子线程0");
            Thread thread1=new Thread(myThread,"子线程1");
            Thread thread2=new Thread(myThread,"子线程2");
            thread0.start();
            thread1.start();
            thread2.start();
/*for(int i=0;i<3;i++){
 new Thread(myThread,"线程"+i).start();
        }
*/
    }
}

结果: 

 

总结:用synchronized(this)以及synchronized()只能防止多个线程执行同一个对象的同步段

synchronized锁的是括号中的对象而非代码

那么怎样才可以使得可以锁住一个对象呢,关键点在MyThread类实现Runnable 接口中只有一个对象syn

1定义Syn类对象syn

2通过有参构造函数实现对MyThread类对象个数的限制

package se.SE.practice;
class Syn{
    public synchronized void test(int ticket){
        System.out.println(Thread.currentThread().getName()+"此方法开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"此方法结束");
    }
}
class MyThread implements Runnable {
    private int ticket = 10;
    private Syn syn;
    public MyThread(Syn syn){
        this.syn=syn;
    }
    @Override
    public void run() {
        this.syn.test(ticket);
    }
}
public class Thread1{
    public static void main(String[] args) {
        Syn syn=new Syn();
        MyThread myThread=new MyThread(syn);
        for(int i=0;i<3;i++){
        new Thread(myThread,"线程"+i).start();
        }
    }
}

输出结果:

达到预期结果!

引入全局锁,从sychronized(this)或者synchronized方法只可以锁住当前对象,锁住代码段需要全局锁

3全局锁

锁代码段

1使用类的静态方法,此时锁住的是当前使用的类,而非对象

也就是把public synchronized void test(int ticket){}方法改为静态方法--->public static synchronized void test(int ticket){}

把类方法锁住--就把代码块锁住

package se.SE.practice;
class Syn{
    public synchronized void test(int ticket){
        System.out.println(Thread.currentThread().getName()+"此方法开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"此方法结束");
    }
}
class MyThread implements Runnable {
    private int ticket = 10;
    private Syn syn;
    @Override
    public void run() {
        this.syn.test(ticket);
    }
}
public class Thread1{
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        for(int i=0;i<3;i++){
        new Thread(myThread,"线程"+i).start();
        }
    }
}

2在同步代码块中,锁当前的class对象,类名称.class

如果是锁普通方法:将普通方法的代码段放在synchronized(Syn.class){}包含的代码段中

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值