博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java学习笔记--线程同步
阅读量:6955 次
发布时间:2019-06-27

本文共 3262 字,大约阅读时间需要 10 分钟。

hot3.png

默认情况下,线程都是独立的,而且异步执行,线程中包含了运行时所需要的数据或方法,而不需要外部的资源或方法,也不必关心其他线程的状态或行为。但是在多个线程在运行时共享数据的情况下,就需要考虑其他线程的状态和行为,否则就不能保证程序的运行结果的正确性。在某些项目中,经常会出现线程同步的问题,即:多个现场在访问同一资源时,会出现安全问题。

所谓同步,Synchronize,就是在发出一个功能调用时,在没有得到结果前,该调用就不返回,同事其他线程也不能调用这个方法。通俗地讲,一个线程能否够抢占cpu,必须考虑另一个线程中的某种条件,而不能随便让操作系统按照默认方式分配cpu,如果条件不具备,就应该等待另一个线程运行,直到条件具备。

一个有问题的案例

案例:有若干张飞机票,用2个线程销售,要求没有票的时候能够提示“没有票了”。以最后剩下3张票为例。首先用传统方法编写这段代码

class TicketRunnable implements Runnable{ private int ticketNum =3; public void run() {  while(true)  {   String tName = Thread.currentThread().getName();   if(ticketNum<=0)   {    System.out.println(tName+" 无票");    break;   }   else   {    ticketNum --;    System.out.println(tName+"卖出一张票,还剩"+ticketNum+"张票");   }  } }}public class ThreadSynTest1{ public static void main(String[] args) {  TicketRunnable r = new TicketRunnable();  Thread th1 = new Thread(r,"线程1");  Thread th2 = new Thread(r,"线程2");  th1.start();  th2.start(); }}

执行结果:

线程1卖出一张票,还剩2张票

线程1卖出一张票,还剩0张票
线程2卖出一张票,还剩1张票
线程1 无票
线程2 无票

从执行的结果看,显然有问题,线程1第二次执行完后,应该是还剩1张票,线程2执行完,应该还剩0张票。

代码改一下,更能说明问题的严重性:

class TicketRunnable implements Runnable{ private int ticketNum =3; public void run() {  while(true)  {   String tName = Thread.currentThread().getName();   if(ticketNum<=0)   {    System.out.println(tName+" 无票");    break;   }   else   {    try{    Thread.sleep(1000);    }    catch(Exception e)    {}    ticketNum --;    System.out.println(tName+"卖出一张票,还剩"+ticketNum+"张票");   }  } }}public class ThreadSynTest1{ public static void main(String[] args) {  TicketRunnable r = new TicketRunnable();  Thread th1 = new Thread(r,"线程1");  Thread th2 = new Thread(r,"线程2");  th1.start();  th2.start(); }}

执行的结果如下:

线程1卖出一张票,还剩2张票

线程2卖出一张票,还剩1张票
线程1卖出一张票,还剩0张票
线程1 无票
线程2卖出一张票,还剩-1张票
线程2 无票

最后一张票被卖出了两次。

问题在于:当只剩下一张票时,线程1卖出了最后一张票,此时线程1已经打印出了 无票,正要执行 break,退出循环,但是此时线程2抢占了cpu,线程2继续其上次的运行,继续卖出票,导致最后一张票被卖出了两次。

更为严重的问题是,改问题的出现具有随机性。以上案例是多个线程消费有限资源的情况,该情况下还有很多其他案例,如多个线程,向有限空间写数据时,线程1写完数据,空间满了,但是还没有来得及告诉系统,两外一个线程就抢占了cpu,也来写数据,不知道空间已经满了,造成溢出。

如何解决此问题

就是让一个线程卖票时,其他线程不能抢占cpu,根据定义,实际上相当于要实现线程的同步,通俗的讲,可以给共享资源加一把锁,这只锁只有一把钥匙,哪个线程获得了这把锁,才有权利访问该共享资源。

在java语言中,synchronized关键字可以解决这个问题,整个语法形式表现为:

synchronized(同步锁对象)

{

 //访问共享资源,需要同步的代码段

}    

注意:synchronized后的"同步锁对象",必须是可以被各个线程共享的,如this、某个全局变量等。不能是一个局部变量。

其原理是:当某一线程运行同步代码段时,在"同步锁对象"上置一标记,运行完这段代码,标记消除。其他线程要想抢占cpu运行这段代码,必须在“同步锁对象”上先检查该标记,只有标记处于消除状态,才能抢占cpu。在上面的例子中,this是一个“同步锁对象”。因此,在上面的案例中,可以将卖票的代码用 synchronized代码包围起来,“同步锁对象”取this。代码如下:

 class TicketRunnable implements Runnable{ private int ticketNum = 3; public void run() {  while(true)  {   String name = Thread.currentThread().getName();   synchronized(this)   {    if(ticketNum<=0)    {     System.out.println(name+"无票");     break;    }    else    {     try     {      Thread.sleep(1000);           }catch(Exception e)     {}     ticketNum --;     System.out.println(name+"卖出一张票,还剩"+ticketNum+"张票");    }   }  } }  }public class ThreadSynTest3{ public static void main(String[] args) {  TicketRunnable tr = new TicketRunnable();  Thread th1 = new Thread(tr,"线程1");  Thread th2 = new Thread(tr,"线程2");  th1.start();  th2.start(); }}

 可以看出,该方法的本质是将需要独占cpu的代码用synchronized(this)包围起来。如前所述,一个线程加上这段代码之后,就在this加上了一个标记,知道该线程净这段代码运行完毕,才释放这个标记。如果其他线程想要抢占cpu,先要检查this上是否有这个标记。若有,就必须等待。

转载于:https://my.oschina.net/kingfrog/blog/268758

你可能感兴趣的文章
内置函数
查看>>
mysql之触发器
查看>>
C 入门 第七节 结构体
查看>>
linux下安装svn
查看>>
Flash Builder快捷键
查看>>
js通过Image和canvas获取图片的base64格式的字符串(只能接受服务器上的图片,不支持本地图片直接转化为base64,因为js没有系统io的权限,js只能操作dom)...
查看>>
转:Unity3D研究院之提取游戏资源的三个工具支持Unity5(八十四)
查看>>
Javascript学习之Window
查看>>
mac 文本编辑器 文本编码Unicode utf-8 不适用的问题
查看>>
如何做好H5性能优化?
查看>>
通过WebClient模拟post上传文件到服务器
查看>>
js判断是否是PC,IOS,Android客户端
查看>>
HTTP
查看>>
sound of the genuine
查看>>
iOS开发之WIFI,3G/4G两种网络同时使用技巧
查看>>
Nodejs基础(5-6)HTTP概念进阶
查看>>
(十)struts2的异常处理机制
查看>>
体验VIP版本灰鸽子,哈哈,拿到了老师的病毒教程
查看>>
倒计时
查看>>
第二次作业
查看>>