线程同步与锁(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/denglusha737/article/details/78353870

   im项目中都会存在离线消息,我们在接受到消息后,开启子线程,处理相关业务逻辑。因为业务逻辑需遵循一定的处理顺序,我们将部分代码加上了锁。但是在离线消息太多时,却出现了线程问题:OutOfMemoryError: pthread_create (1040KB stack) failed: Try again 本地创建线程时发现内存不够,栈内存溢出。花费了不少时间,一方面添加了队列,另一方面将项目中的同步代码好好梳理了一遍,总算将这个问题解决了。在这里,将线程同步的知识点梳理梳理,记录如下:

 

   1.关键字synchronize修饰的同步方法,它的锁对象是谁?

    答:同步方法的锁对象是调用者本身。如方法加锁后,对象p在不同线程调用该方法,会造成堵塞。

   

    public void useMethodInThread(final int type, final String tag) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                switch (type) {
                    case 1:
                        method1(tag);
                        break;
                    case 2:
                        method2(tag);
                        break;
                    case 3:
                        method3(tag);
                        break;
                    case 4:
                        method4(tag);
                        break;
                    case 5:
                        method5(tag);
                        break;
                    default:
                        break;
                }
            }
        }).start();
    }

    /**
     * 同步方法
     */
    public synchronized void method1(final String str) {

        Log.i(tag, "start method1  " + str);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            Log.i(tag, "start method1 after sleep  " + str);
        }
    }
   //调用
   useMethodInThread(1, "001");
  useMethodInThread(1, "002");

  日志:

  10-26 15:18:32.572  I/TestLockActivity: start method1  001
  10-26 15:18:34.572  I/TestLockActivity: start method1 after sleep  001
  10-26 15:18:34.572  I/TestLockActivity: start method1  002
  10-26 15:18:36.572  I/TestLockActivity: start method1 after sleep  002

  第一次调用后,该方法被堵塞,直到调用完毕后,才进行第二次的调用。没毛病。


  2.同步方法与同步代码块synchronized(this),会造成堵塞吗?

扫描二维码关注公众号,回复: 4426495 查看本文章

  答:同步代码块synchronized(this)使用了当前对象作为锁对象,与同步方法中的锁对象一致,会造成线程堵塞。

 public void useMethodInThread(final int type, final String tag) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                switch (type) {
                    case 1:
                        method1(tag);
                        break;
                    case 2:
                        method2(tag);
                        break;
                    default:
                        break;
                }
            }
        }).start();
    }
  /**
     * 同步代码块
     */
    public void method2(String str) {
        synchronized (this) {
            Log.i(tag, "start method2 " + str);
        }
    }
   //调用
   useMethodInThread(1, "001");
   useMethodInThread(2, "002");
 
  日志:

  10-26 15:38:05.342  I/TestLockActivity: start method1  001
  10-26 15:38:07.342  I/TestLockActivity: start method1 after sleep  001
  10-26 15:38:07.392  I/TestLockActivity: start method2 002

   同步方法中使用了线程sleep2秒,此时同步代码块一直在等待锁释放后,才执行代码。


   3.同步代码块后的非同步代码,会在同步堵塞时,优先执行吗?

   答:不会。这就像跑步,同步代码块是独行桥,桥上堵塞后,哪怕桥后的是多跑道,依然需要等待前面的先过桥。

   /**
     * 同步代码与非同步代码
     */
    byte[] bytes = new byte[1];

    public void method3(String str) {
        Log.i(tag, "start method3 first   " + str);
        synchronized (bytes) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                Log.i(tag, "start method3  synchronized " + str);
            }
        }
        Log.i(tag, "start method3  last " + str);
    }
  //调用:
    useMethodInThread(3, "001");
    useMethodInThread(3, "002");

  日志:

    10-26 16:02:59.522  I/TestLockActivity: start method3 first   001
   10-26 16:02:59.522  I/TestLockActivity: start method3 first   002
   10-26 16:03:01.522  I/TestLockActivity: start method3  synchronized 001
   10-26 16:03:01.522  I/TestLockActivity: start method3  last 001
   10-26 16:03:03.572  I/TestLockActivity: start method3  synchronized 002
   10-26 16:03:03.572  I/TestLockActivity: start method3  last 002

    第二次调用时,在同步代码块中造成堵塞,释放锁后,各子线程独立运行。


  4.同步方法中添加handler延迟,会释放锁对象吗?

  答:会。创建handler时,一般需要设置looper,默认使用主线程的looper,这时线程切换,释放了锁对象。

 private synchronized void method4(String str) {
        Log.i(tag, "start method4 first   " + str);
        Message obtain = Message.obtain();
        obtain.what = 0;
        obtain.obj = str;
        handler.sendMessageDelayed(obtain, 2000);
    }

 Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 0) {
                Log.i(tag, "start method4 handler  " + msg.obj);
            }
        }
    };
  //调用:
   useMethodInThread(4, "001");
  useMethodInThread(4, "002");

  日志:

  10-26 16:25:19.312 I/TestLockActivity: start method4 first   001
 10-26 16:25:19.312  I/TestLockActivity: start method4 first   002
 10-26 16:25:21.312  I/TestLockActivity: start method4 handler  001
 10-26 16:25:21.312  I/TestLockActivity: start method4 handler  002

 

 5.同步方法中添加timer延迟,会释放锁对象吗?

  答:会。timer内部应该也做了线程切换,释放了锁对象。

 private synchronized void method5(String str) {
        Log.i(tag, "start method5 first   " + str);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            Log.i(tag, "start method5  sleep   " + str);
        }
        runTimer(str);
    }

     private void runTimer(final String str) {
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                Log.i(tag, "start method5  timerTask " + str);
            }
        };
        Timer timer = new Timer();
        timer.schedule(timerTask, 2000);
    }
   //调用
   useMethodInThread(5, "001");
  useMethodInThread(5, "002");

  日志:

  10-26 16:45:09.912  I/TestLockActivity: start method5 first   001
10-26 16:45:11.912  I/TestLockActivity: start method5  sleep   001
10-26 16:45:11.962  I/TestLockActivity: start method5 first   002
10-26 16:45:13.912  I/TestLockActivity: start method5  timerTask 001
10-26 16:45:13.962  I/TestLockActivity: start method5  sleep   002
10-26 16:45:15.962  I/TestLockActivity: start method5  timerTask 002

 启动timer后,立即释放了锁,第二次调用的代码立即执行,打印了method5 first   002,后面的timer部分代码在各子线程中分别执行。

 

  写在最后:加锁同步只在多线程中才有存在的意义,单线程中必然是顺序执行。将线程看作是跑道,单跑道就不存在超越的问题。多线程就是多条跑到,同步代码或者同步方法就是跑到交叉处,必须容前面的先通过,后面的才能动身。

 



猜你喜欢

转载自blog.csdn.net/denglusha737/article/details/78353870
今日推荐