码迷,mamicode.com
首页 > 编程语言 > 详细

Java多线程之代码同步

时间:2020-03-22 12:27:00      阅读:99      评论:0      收藏:0      [点我收藏+]

标签:rri   问题   end   xxx   方式   数据同步   代码   相同   没有   

 

多线程共享数据(多个线程共同访问相同的数据),需要进行数据同步,保证同一数据、同一时刻只能被一个线程访问。

使用同步是为了防止多个线程同一时刻对同一数据进行读写,如果对同一数据数据都只进行读操作、不进行修改,则不必使用同步。

 

 

以售票为例   不使用同步

public class SaleTicketThread extends Thread {
    private static int ticket = 100; //票数,static
    
    @Override
    public void run() {
        while (ticket > 0) {
            System.out.println("售出第" + (100 - ticket + 1) + "个座位");
            ticket--;
        }
    }

}

 

public class SaleTicket {

    public static void main(String[] args) {
        SaleTicketThread thread1 = new SaleTicketThread();
        thread1.start();

        SaleTicketThread thread2 = new SaleTicketThread();
        thread2.start();

        SaleTicketThread thread3 = new SaleTicketThread();
        thread3.start();
    }

}

启动三个线程来售票,这3个线程都要访问同一个数据 SaleTicketThread.ticket  剩余票数

 

技术图片

很容易出问题

 

 


 

 

 

三种常见的同步方式

  • 同步方法   默认用当前对象(this)或者当前类的class对象作为锁,会给整个方法都加锁
  • 同步代码块   可以选择以什么作为锁,可以只同步会发生同步问题的部分代码而不是整个方法,比同步方法更加细粒度
  • ReentrantLock自己加锁、释放锁,可以只给部分代码加锁,比同步方法更加细粒度

 

 

1、同步方法

public class SaleTicketThread extends Thread {
    private static int ticket = 100; //票数,static

    protected synchronized void saleTicket(){ //用synchronized修饰方法
        while (ticket > 0) { 
            System.out.println("售出第" + (100 - ticket + 1) + "个座位");
            ticket--;
        }
    }
    
    @Override
    public void run() {
        saleTicket();
    }

}

如果方法没有使用static修饰,是实例方法,默认以this(当前对象)作为锁;

如果方法是静态方法,默认以当前类的class对象作为锁。

 

 

 

2、同步代码块

public class SaleTicketThread extends Thread {
    private static Integer ticket = 100; //票数,static

    @Override
    public void run() {
        synchronized (ticket){ //锁要是Object类型
            while (ticket > 0) {
                System.out.println("售出第" + (100 - ticket + 1) + "个座位");
                ticket--;
            }
        }
    }

}

同步代码块可以自己决定要使用的锁对象,可以直接把共享数据作为锁,也可以新建一个Object对象作为锁。

 

要注意2点:

1、锁要是Object类型,int、float、char等基本类型没有继承Object,不能作为锁,要使用对应的包装类型。String继承了Object,可以作为锁。

2、访问这一共享数据的线程,使用的锁对象要相同(对象地址要相同)。

 

 

 

3、ReentrantLock

public class SaleTicketThread extends Thread {
    private static Integer ticket = 100; //票数,static
    private final static ReentrantLock lock=new ReentrantLock(); //锁对象,final static修饰,

    @Override
    public void run() {
        lock.lock(); //加锁
        while (ticket > 0) {
            System.out.println("售出第" + (100 - ticket + 1) + "个座位");
            ticket--;
        }
        lock.unlock(); //释放锁
    }

}

以ReentrantLock对象作为锁,注意所有要使用此共享数据的线程使用的应该是同一个ReentrantLock对象(要是同一个锁对象)。

其实和同步代码块差不多,lock()标志同步代码块开始,unlock()标志同步代码块结束。

 

 

可能发生异常的情况:

public class XxxThread extends Thread {
    private final static ReentrantLock lock=new ReentrantLock(); //锁对象,final static修饰

    @Override
    public void run() {
        //...
        
        lock.lock(); //加锁
        try{
            //......  //放在try中
        }catch ( ){
            //.....
        }finally {
            lock.unlock(); //在finally中释放锁
        }

        //.....
    }

}

 

 

 

 

说明

可以把共享数据、锁可以放在某个线程中作为静态变量,

如果要在不同的线程类中使用,比如在查询、售票2个线程类中都要使用剩余票数这个成员变量,那就声明为public static,暴露出来;

如果只在一个线程类中使用(此线程类的多个实例共享此数据),设置成private static即可。

 

也可以把共享数据、锁放在父线程(开启它们的线程)中,在线程类的构造函数中传入共享数据、锁对象。

 

总之,要所有访问这个共享数据的线程都可以访问到共享数据、锁。

 

访问这一共享数据的所有线程,使用的锁对象要相同(对象地址要相同)。

 

Java多线程之代码同步

标签:rri   问题   end   xxx   方式   数据同步   代码   相同   没有   

原文地址:https://www.cnblogs.com/chy18883701161/p/12545262.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!