标签: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 剩余票数
很容易出问题
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对象作为锁。
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、访问这一共享数据的线程,使用的锁对象要相同(对象地址要相同)。
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即可。
也可以把共享数据、锁放在父线程(开启它们的线程)中,在线程类的构造函数中传入共享数据、锁对象。
总之,要所有访问这个共享数据的线程都可以访问到共享数据、锁。
访问这一共享数据的所有线程,使用的锁对象要相同(对象地址要相同)。
标签:rri 问题 end xxx 方式 数据同步 代码 相同 没有
原文地址:https://www.cnblogs.com/chy18883701161/p/12545262.html