1. 程式人生 > >多線程編程-- part 3 多線程同步->synchronized關鍵字

多線程編程-- part 3 多線程同步->synchronized關鍵字

靜態方法 mage rri pri alt image col ring 釋放

多線程同時訪問一個資源,可以會產生不可預料的結果,所以為這個資源加鎖,訪問資源的第一個線程為其加鎖後,其他線程便不能在使用那個資源,直到鎖被解除。

舉個例子:

存款1000元,能取出800的時候我就取800,當我同時用兩個線程調用這個取錢操作時,有時可以取出1600元

static class HelloRunable implements Runnable{
         private int money = 1000;

         //取出800元
          int getMoney() {
             System.out.println("開始" + money);
             if(money > 800) {
                 try {
                     Thread.sleep(100);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 money -= 800;
                 System.out.println("我取了800元" + money);
             }
             System.out.println("結束" + money);
             return money;
         }

        @Override
        public void run() {
            money = getMoney();
        }
    }


    public static void main(String[] args) {
        HelloRunable helloRunable = new HelloRunable();
        Thread t = new Thread(helloRunable);
        Thread t1 = new Thread(helloRunable);
        t.start();
        t1.start();

    }

  技術分享

synchronized:

所以我們引入了同步機制,在取錢方法前面加入了synchronized關鍵字,該方法稱為同步方法,輸出結果為

技術分享

java中每個對象都有一個鎖,或者叫做監視器(monitor),當一個線程訪問某個對象的synchronized方法時,將該對象上鎖,其他任何線程都無法再去訪問該對象的synchronized方法,直到之前的那個線程執行完方法之後,才會將對象鎖釋放,其他線程才可以在用該對象的synchronized方法。 註:這是給對象上鎖,如果是不同的對象則該對象之間沒有限制關系。

如果一個對象有多個synchronized方法,某一時刻某個線程已經進入到了synchronized方法,那麽在該方法沒有執行完前,其他線程是無法訪問該對象的任何synchronized方法的。

例如:

 public static void main(String[] args)
    {
        Example example = new Example();

        Thread t1 = new Thread1(example);
        Thread t2 = new Thread2(example);

        t1.start();
        t2.start();
    }



    static class Example
    {
        public synchronized void execute()
        {
            for (int i = 0; i < 20; ++i)
            {
                try
                {
                    Thread.sleep((long) Math.random() * 1000);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                System.out.println("Hello: " + i);
            }
        }

        public  void execute2()
        {
            for (int i = 0; i < 20; ++i)
            {
                try
                {
                    Thread.sleep((long) Math.random() * 1000);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                System.out.println("World: " + i);
            }
        }

    }

    static class Thread1 extends Thread
    {
        private Example example;

        public Thread1(Example example)
        {
            this.example = example;
        }

        @Override
        public void run()
        {
            example.execute();
        }

    }

    static class Thread2 extends Thread
    {
        private Example example;

        public Thread2(Example example)
        {
            this.example = example;
        }

        @Override
        public void run()
        {
            example.execute2();
        }

    }

  技術分享

當加上synchronized後,當同步方法未執行完時另一個同步方法不會執行

技術分享

靜態的同步方法

  當一個synchronized關鍵字修飾的方法同時被static修飾,非靜態的同步方法會將對象上鎖,但是靜態方法不屬於對象,而是屬於類,他會將這個方法所在的類的Class對象上鎖。

 public static void main(String[] args)
    {
        Example example = new Example();

        Thread t1 = new Thread1(example);

        example = new Example();
        Thread t2 = new Thread2(example);

        t1.start();
        t2.start();
    }



    static class Example
    {
        public static synchronized void execute()
        {
            for (int i = 0; i < 20; ++i)
            {
                try
                {
                    Thread.sleep((long) Math.random() * 1000);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                System.out.println("Hello: " + i);
            }
        }

        public static synchronized void execute2()
        {
            for (int i = 0; i < 20; ++i)
            {
                try
                {
                    Thread.sleep((long) Math.random() * 1000);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                System.out.println("World: " + i);
            }
        }

    }

    static class Thread1 extends Thread
    {
        private Example example;

        public Thread1(Example example)
        {
            this.example = example;
        }

        @Override
        public void run()
        {
            example.execute();
        }

    }

    static class Thread2 extends Thread
    {
        private Example example;

        public Thread2(Example example)
        {
            this.example = example;
        }

        @Override
        public void run()
        {
            example.execute2();
        }

    }

技術分享

不加靜態方法方法:

技術分享

  當某個synchronized方法是static的,那麽當線程訪問該方法時,他鎖的並不是synchronized方法所在的對象,而是synchronized方法所在類的對應的class對象(java中,一個類無論有多少對象,這些對象會對應唯一的class對象,因此當線程分別訪問一個類的兩個對象的的兩個static synchronized方法時,他們的執行順序也是有順序的,也就是說一個線程先去執行,執行完釋放鎖,另一個線程再去執行)

synchronized代碼塊

synchronized(object) {

}

  表示的是在執行過程中會將object對象上鎖(這個對象可以使任意類的對象,也可以使用this),可以自行規定上鎖對象

public class test {
    public static void main(String[] args) {
        Example example = new Example();

        Thread t1 = new Thread1(example);
        Thread t2 = new Thread2(example);

        t1.start();
        t2.start();
    }

}

    class Example {
        private Object object = new Object();

        public void execute() {
            System.out.println("我開始了");
            synchronized (object) {
                for (int i = 0; i < 20; ++i) {
                    try {
                        Thread.sleep((long) Math.random() * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Hello: " + i);
                }

            }

        }

        public void execute2() {
            System.out.println("我也開始了");
            synchronized (object) {
                for (int i = 0; i < 20; ++i) {
                    try {
                        Thread.sleep((long) Math.random() * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("World: " + i);
                }

            }

        }

    }

    class Thread1 extends Thread {
        private Example example;

        public Thread1(Example example) {
            this.example = example;
        }

        @Override
        public void run() {
            example.execute();
        }

    }

    class Thread2 extends Thread {
        private Example example;

        public Thread2(Example example) {
            this.example = example;
        }

        @Override
        public void run() {
            example.execute2();
        }

    }

技術分享

可以看到除了synchronized代碼塊裏的方法,其他是不會同步執行的。

  synchronized方法是一種粗粒度的並發控制,某一時刻,只能有一個線程執行synchronized方法。

  synchronized塊則是細粒度的並發控制,只會將塊中的代碼同步,方法內的其他代碼是可以被多個線程同時訪問到的

多線程編程-- part 3 多線程同步->synchronized關鍵字