博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
11_控制线程_多线程同步
阅读量:4627 次
发布时间:2019-06-09

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

【线程安全问题例子】

模拟售票案例,4个窗口售票,总共100张票。

public class Demo {    public static void main(String[] args) {        SaleThread sa=new SaleThread();                Thread t1=new Thread(sa);        Thread t2=new Thread(sa);        Thread t3=new Thread(sa);        Thread t4=new Thread(sa);                t1.start();        t2.start();        t3.start();        t4.start();    }}class SaleThread implements Runnable{    private int tickets=100;    //总共100张票        @Override    public void run() {        while(tickets>0){            try {                Thread.sleep(10);    //此处休眠10ms             } catch (InterruptedException e) {                e.printStackTrace();            }              System.out.println(Thread.currentThread().getName()+"----卖出的票号码:"+tickets--);        }    }}

【运行结果】

【分析】

当只剩下一张票时,某个线程判断满足while(tickets>0)进入循环,然后休眠10ms,此时其它线程在这10ms内依次进入判断while(tickets>0),然后都休眠10ms,最后大家都执行最后的System.out.println(Thread.currentThread().getName()+"----卖出的票号码:"+tickets--);,直接导致出现了结果中出错的几种情况。

 

【同步的基础】

Java中的每一个对象都可以作为锁。

同步方法     :锁是当前实例对象。

静态同步方法:锁是当前对象的Class对象。

同步代码块   :锁是synchronized括号里配置的对象。

 

【同步代码块】

想要解决上面的线程安全问题,必须保证处理共享资源的代码在任何时刻只能有一个线程访问。

当多个线程使用同一个共享资源的时候,可以将处理共享资源的代码放在一个代码块中,使用synchronized关键字来修饰,被称作同步代码块

synchronized(lock){    //操作共享资源的代码块}

lock:是一个锁对象,是执行同步代码块的关键,当线程执行同步代码块时,首先会检查锁对象的标志位,默认情况下标志位为1,此时线程会执行同步代码块,同时将锁对象置为0。当一个新的线程执行到这段同步代码块时,由于锁对象的标志位为0,该新线程会发生阻塞,等待当前线程执行完同步代码块时,锁对象的标志位重新置为1,新线程才能进入同步代码块执行其中的代码。循环往复,直到共享资源被处理完为止。

【加同步代码块解决线程安全问题案例】

public class Demo {    public static void main(String[] args) {        SaleThread sa=new SaleThread();                Thread t1=new Thread(sa);        Thread t2=new Thread(sa);        Thread t3=new Thread(sa);        Thread t4=new Thread(sa);                t1.start();        t2.start();        t3.start();        t4.start();    }}class SaleThread implements Runnable{    private int tickets=100;    //总共100张票        @Override    public void run() {        while(true){            synchronized (this) {                try {                    Thread.sleep(10);    //此处休眠10ms                 } catch (InterruptedException e) {                    e.printStackTrace();                }                  if(tickets>0){                    System.out.println(Thread.currentThread().getName()+"----卖出的票号码:"+tickets--);                }else{                    break;                }            }        }    }}

【运行结果】

...............................

 

【同步方法】

在方法前面加上synchronized关键字修饰,被修饰的方法称为同步方法,它能使实现和同步代码快同样的功能。

synchronized  返回值类型  方法名([参数1,......]){}

被synchronized修饰的方法在某一时刻只允许一个线程访问,访问该方法的其它线程都会发生阻塞,直到当前线程访问完毕后,其它线程才有机会执行方法。

【同步方法的案例】

public class Demo {        public static void main(String[] args) {        TicketsThread tt=new TicketsThread();                Thread t1=new Thread(tt);        Thread t2=new Thread(tt);        Thread t3=new Thread(tt);        Thread t4=new Thread(tt);                t1.start();        t2.start();        t3.start();        t4.start();    }}class TicketsThread implements Runnable{    private int tickets =100;        @Override    public void run() {        while(true){            saleTickets();  //调用同步方法            if(tickets<=0){                break;            }        }    }    //定义一个同步方法 saleTickets()    private synchronized void saleTickets(){        if(tickets>0){            try {                Thread.sleep(10);   //所有的线程在这里都要休眠10ms            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(Thread.currentThread().getName()+"----卖出的票号码:"+tickets--);        }    }}

【运行结果】

......

【分析】

同步方法其实也有自己的锁,它的锁就是当前调用该方法的对象,也就是this指向的对象。

这样做的好处是:同步方法被所有线程共享,方法所在的对象相对于所有线程来说都是唯一的,从而保证了锁方法的唯一性。

【扩展:静态方法怎么处理?】

静态方法可以使用"类名.方法名()"方式直接被调用,调用静态方法无需创建对象,如果不创建对象,静态方法的锁就不会是this,JAVA中静态方法的锁该方法所在类的class对象,该对象可以直接使用“类名.class”的方式获取。

 

【同步代码块、同步方法的优缺点】

[ 优点 ]

解决了多个线程同时访问共享数据时的安全问题,只要加上一个锁,在同一个时间内只能有一条线程执行。

[ 缺点 ]

线程执行同步代码块的时都要判断锁的状态,非常消耗资源,效率非常低。

 

转载于:https://www.cnblogs.com/HigginCui/p/6136287.html

你可能感兴趣的文章
WebService原理
查看>>
【Unity 3D】学习笔记三十七:物理引擎——碰撞与休眠
查看>>
js动态删除div元素
查看>>
计算机网络中的TCP/IP模型
查看>>
spring mvc 自定义Handlermapping
查看>>
JS验证密码安全级别
查看>>
Cookie是可以覆盖的,如果重复写入同名的Cookie,那么将会覆盖之前的Cookie。
查看>>
高并发 Nginx+Lua OpenResty系列(11)——流量复制/AB测试/协程
查看>>
高并发 Nginx+Lua OpenResty系列(8)——Lua模版渲染
查看>>
跟我学SpringCloud | 第三篇:服务的提供与Feign调用
查看>>
高并发 Nginx+Lua OpenResty系列(9)——HTTP服务
查看>>
跟我学SpringCloud | 第五篇:熔断监控Hystrix Dashboard和Turbine
查看>>
高并发 Nginx+Lua OpenResty系列(10)——商品详情页
查看>>
跟我学SpringCloud | 第七篇:Spring Cloud Config 配置中心高可用和refresh
查看>>
openGL 六边形
查看>>
openGL 蓝天白云
查看>>
openGL 画线条
查看>>
pyqt5desinger的安装即配置
查看>>
openGL 折线
查看>>
python 通过函数的使用,将字典的深度搜索化简(减少循环次数)
查看>>