专栏首页mathor线程同步

线程同步

 对访问同一个资源的多个线程进行协调的过程,就叫线程同步  用一个简单的例子讲述线程同步问题:  小明账户里有3000元钱,他拿存折去银行取2000,银行的机器首先判断账户里的钱够不够2000,判断够。与此同时,小明的老婆拿着小明的银行卡去了另一家银行取钱,也取2000,判断也够,小明的老婆取出来了,账户里的钱变为了1000,这时小明这边的机器已经判断过足够了,所以小明也取出了2000,账户的钱变为1000。最终的结果就是,小明和他老婆分别取了2000块钱,账户还剩1000。  解决这个问题的方法也很简单,只要规定,访问统一账户的人同时只能有一个。就比方说,两台电脑不能同时登陆同一个qq  下面先看一个示例

public class TestSync implements Runnable{
    Timer timer = new Timer();
    public static void main(String[] args) {
        TestSync test = new TestSync();
        Thread t1 = new Thread(test);
        Thread t2 = new Thread(test);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        t2.start();
    }
    public void run() {
        timer.add(Thread.currentThread().getName());
    }
}
class Timer{
    private static int num = 0;
    public void add(String name) {
        num++;
        try {
            Thread.sleep(1);
        }catch(InterruptedException e) {}
        System.out.println(name + ",你是第" + num + "个使用timer的线程");
    }
}

 首先用一个内存图分析一下整个程序:

 输出结果为:

t1,你是第2个使用timer的线程
t2,你是第2个使用timer的线程

 分析一下这个程序的执行:  首先一个线程在执行add方法的过程中,执行了一次num++,此时num的值是1,然后当前线程sleep,另一个线程开始执行add方法,又执行了一次num++,此时num的值是2,然后这个线程sleep。上一个线程sleep结束了,输出,num的值就是2,然后另一个线程sleep也结束了,输出,num的值也是2  其实这就跟前面的取钱例子一样,解决办法就是给add方法加一把锁,让他同时只能有一个线程访问,不论当前线程是否睡眠,只有当访问add方法的线程结束了,才能允许另一个线程访问

public class TestSync implements Runnable{
    Timer timer = new Timer();
    public static void main(String[] args) {
        TestSync test = new TestSync();
        Thread t1 = new Thread(test);
        Thread t2 = new Thread(test);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        t2.start();
    }
    public void run() {
        timer.add(Thread.currentThread().getName());
    }
}
class Timer{
    private static int num = 0;
    public void add(String name) {
        synchronized (this) {
            num++;
            try {
                Thread.sleep(1);
            }catch(InterruptedException e) {}
            System.out.println(name + ",你是第" + num + "个使用timer的线程");
        }
    }
}

 另一种写法,直接给方法加锁

public class TestDeadLock implements Runnable{
    public int flag = 1;
    static Object o1 = new Object(),o2 = new Object();
    public void run() {
        System.out.println("flag = " + flag);
        if(flag == 1) {
            synchronized(o1) {
                try {
                    Thread.sleep(500);
                }catch(InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized(o2) {
                    System.out.println("1");
                }
            }
        }
        if(flag == 0) {
            synchronized(o2) {
                try {
                    Thread.sleep(500);
                }catch(Exception e) {
                    e.printStackTrace();
                }
                synchronized(o1) {
                    System.out.println("2");
                }
            }
        }
    }
    public static void main(String[] args) {
        TestDeadLock td1 = new TestDeadLock();
        TestDeadLock td2 = new TestDeadLock();
        td1.flag = 1;
        td2.flag = 0;
        Thread t1 = new Thread(td1);
        Thread t2 = new Thread(td2);
        t1.start();
        t2.start();
    }
}

&esmp;运行结果如下图:

 首先分别给两个线程内的flag变量分别赋值为1和0,那么他们就会分别执行对应的if语句。对于flag = 1来说,它先锁住o1,让它sleep500毫秒,只要再锁住o2,就可以打印1了;对于flag = 0来说,它先锁住o2,让它sleep500毫秒,只要再锁住o1,就可以打印2了,所以他们分别锁住一个线程对象,等待另一个线程对象,但是他们永远不可能锁住另一个线程对象了,所以程序进入了死锁

一道面试题

public class TT implements Runnable{
    static int b = 100;
    public synchronized static void m1() throws InterruptedException {
        b = 1000;
        Thread.sleep(5000);
        System.out.println("b = " + b);
    }
    public static void m2() {
        b = 2000;
        System.out.println(b);
    }
    public void run() {
        try {
            m1();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        TT tt = new TT();
        Thread t = new Thread(tt);
        t.start();
        Thread.sleep(1000);
        tt.m2();
    }
}

 上述程序输出结果是什么?  正确答案:

2000
b = 2000

面试题变形

public class TT implements Runnable{
    static int b = 100;
    public synchronized static void m1() throws InterruptedException {
        b = 1000;
        Thread.sleep(5000);
        System.out.println("b = " + b);
    }
    public synchronized static void m2() throws InterruptedException {
        Thread.sleep(2500);
        b = 2000;
    }
    public void run() {
        try {
            m1();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        TT tt = new TT();
        Thread t = new Thread(tt);
        t.start();
        tt.m2();
    }
}

 最后结果是:b = 1000  用一段文字解释一下上面两个问题,首先是第一个程序,m1方法带锁,m2不带锁,主线程中创建线程t,t调用m1方法,将b的值改为1000,然后sleep,此时主线程也在继续执行,调用了m2方法,将b的值改为2000,打印b。随后m1继续执行,打印的b当然就是2000  对于第二个程序,m1和m2两个方法都带锁,那么这两个方法就不能被同时访问,同时只能有一个方法在执行,另一个方法会等待。首先t开始,然后主线程继续执行m2,此时m1是不能会被执行的,因为两个方法都带锁,m2执行完,b的值被改为2000,然后执行m1,b的值又被改为1000

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 事件模型

     事件模型如何理解呢,举个例子,你老婆出门了,让你在家看孩子,难道你每过一分钟就去看一次孩子吗,对于计算机来说,我做一个按钮,难道就一直监听这个按钮使用没有...

    mathor
  • 线程的基本概念

    mathor
  • TextField和Graphics类

     这段小程序最重要的部分在于,我需要将TFFrame类中的tf相关信息传到TFActionListener中,这样才能使用tf对象的一些方法,比方说获取其文本框...

    mathor
  • JUC 多线程基础

    如果一个线程对共享变量的修改,能够被其它线程看到,那么就能说明共享变量在线程之间是可见的。如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线...

    万能青年
  • java 线程之对象的同步和异步(实例讲解)

    下面小编就为大家带来一篇java 线程之对象的同步和异步(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    挨踢小子部落阁
  • 如何创建多线程

    Wait: 等待状态,没有通过notify 或者 notifyAll 唤醒,就会一直进行等待。

    chaplinthink
  • 编写高性能的Java代码需要注意的4个问题

    ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个Th...

    宜信技术学院
  • Java多线程JUC

    1. volatile 关键字 多线程访问的时候,一个比较严重的问题就是内存不可见,其实在内存访问的时候每一个线程都有一个自己的缓冲区,每次在做修改的时候都是从...

    lwen
  • C++-面向对象(六)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    cwl_java
  • java应用CAS

      CAS(Compare and Swap),即比较并替换。jdk里的大量源码通过CAS来提供线程安全操作,比如AtomicInteger类。下面我们来分析一...

    良辰美景TT

扫码关注云+社区

领取腾讯云代金券