志在指尖
用双手敲打未来

Java多线程之线程同步

多线程同享数据(多个线程共同拜访相同的数据),需求进行数据同步,保证同一数据、同一时间只能被一个线程拜访。
运用同步是为了避免多个线程同一时间对同一数据进行读写,假如对同一数据数据都只进行读操作、不进行修正,则不用运用同步。
以售票为例不运用同步
publicclassSaleTicketThreadextendsThread{privatestaticintticket=100;//票数,static@Overridepublicvoidrun(){while(ticket>0){
System.out.println(“售出第”+(100-ticket+1)+”个座位”);
ticket–;
}
}
}
publicclassSaleTicket{publicstaticvoidmain(String[]args){
SaleTicketThreadthread1=newSaleTicketThread();
thread1.start();
SaleTicketThreadthread2=newSaleTicketThread();
thread2.start();
SaleTicketThreadthread3=newSaleTicketThread();
thread3.start();
}
}
启动三个线程来售票,这3个线程都要拜访同一个数据SaleTicketThread.ticket剩下票数
很简单出问题
三种常见的同步办法
同步办法默认用当时目标(this)或许当时类的class目标作为锁,会给整个办法都加锁
同步代码块能够选择以什么作为锁,能够只同步会发生同步问题的部分代码而不是整个办法,比同步办法愈加细粒度
ReentrantLock以ReentrantLock目标作为锁,能够只给部分代码加锁,比同步办法愈加细粒度Java
1、同步办法
publicclassSaleTicketThreadextendsThread{privatestaticintticket=100;//票数,staticprotectedstaticsynchronizedvoidsaleTicket(){//用synchronized润饰办法while(ticket>0){
System.out.println(“售出第”+(100-ticket+1)+”个座位”);
ticket–;
}
}
@Overridepublicvoidrun(){
saleTicket();
}
}
假如办法没有运用static润饰,是实例办法,默认以this(当时目标)作为锁,假如此线程类有多个实例(多条线程),这些线程运用的锁不是同一个目标,起不到同步的效果。
假如办法是静态办法,默认以当时类的class目标作为锁,假如此线程类有多个实例(多条线程),这些线程运用的锁是同一个目标,实现了同步,显然应该运用static。
同步办法有一个很大的问题:只效果于当时线程类,比如查询、售票2个线程类都要运用剩下票数这个同享数据,都运用同步办法,锁目标不同,这2个线程类之间并没有同步。
所以同步办法一般用于此同享数据只效果于一个线程类的多个实例。
锁的规模是固定(整个办法),不能修正,不灵活;运用状况还有约束,有点鸡肋。
相比之下,同步代码块、ReentrantLock的运用不受约束,强大多了。
2、同步代码块
publicclassSaleTicketThreadextendsThread{privatestaticIntegerticket=100;//票数,static@Overridepublicvoidrun(){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
publicclassSaleTicketThreadextendsThread{privatestaticIntegerticket=100;//票数,staticprivatefinalstaticReentrantLocklock=newReentrantLock();//锁目标,finalstatic润饰,@Overridepublicvoidrun(){lock.lock();//加锁while(ticket>0){
System.out.println(“售出第”+(100-ticket+1)+”个座位”);
ticket–;
}lock.unlock();//开释锁}
}
以ReentrantLock目标作为锁,注意一切要运用此同享数据的线程运用的应该是同一个ReentrantLock目标(要是同一个锁目标)。
其实和同步代码块差不多,lock()标志同步代码块开始,unlock()标志同步代码块完毕。
可能发生反常的状况:
publicclassXxxThreadextendsThread{privatefinalstaticReentrantLocklock=newReentrantLock();//锁目标,finalstatic润饰@Overridepublicvoidrun(){//…lock.lock();//加锁try{//……//放在try中}catch(){//…..}finally{
lock.unlock();//在finally中开释锁,避免发生反常后锁没有开释,其它线程一直等候}//…..}
}
说明
能够把同享数据、锁能够放在某个线程中作为静态变量,
假如要在不同的线程类中运用,比如在查询、售票2个线程类中都要运用剩下票数这个成员变量,那就声明为publicstatic,暴露出来;
假如只在一个线程类中运用(此线程类的多个实例同享此数据),设置成privatestatic即可。
也能够把同享数据、锁放在父线程(开启它们的线程)中,在线程类的结构函数中传入同享数据、锁目标。
总之,要一切拜访这个同享数据的线程都能够拜访到同享数据、锁。

未经允许不得转载:IT技术网站 » Java多线程之线程同步
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载