Java多线程学习篇(二)synchronized

synchronized 有二种修饰方法:
  1. 修饰一个方法

    synchronized public void runTest{
        /**/
    }
  2. 修饰一个代码块

    public void runTest{
        synchronized( /*某一对象或某一类*/ ){
            /* 代码块 */
        }
    }
synchronized 的作用范围分为修饰一个类和修饰一共对象
当修饰一个对象时,不同线程的同一对象调用相同代码会发生堵塞
当修饰一个时,不同线程的同一类调用相同代码会发生堵塞
修饰静态方法相当于修饰类

定义一个类(用于验证 synchronized 的作用范围)

public class Test implements Runnable {

    public static int Count = 0;

    @Override
    public void run() {
        runTest();
    }

    public void runTest() {
        for (int i = 0; i < 5; ++i) {
            Count++;
            System.out.println(Thread.currentThread().getName() + " " + Count);
        }
    }
}

当 synchronized 修饰一个方法时

  1. 若方法为非静态方法,作用的范围是一个对象

    不同线程的同一对象调用该方法时会发生堵塞

    //调用相同的对象
    synchronized public void runTest() { // 修饰非静态的方法 for (int i = 0; i < 5; ++i) { Count++; System.out.println(Thread.currentThread().getName() + " " + Count); } }

    通过以下代码调用

    Test test = new Test();
    Thread thread_one = new Thread( test, "Thread_ONE" );
    Thread thread_two = new Thread( test, "Thread_Two" );
    thread_one.start();
    thread_two.start();

    结果是

    Thread_ONE 1
    Thread_ONE 2
    Thread_ONE 3
    Thread_ONE 4
    Thread_ONE 5
    Thread_Two 6
    Thread_Two 7
    Thread_Two 8
    Thread_Two 9
    Thread_Two 10

    由于该调用是二个thread任务中的对象是同一个test,第一个thread任务运行时,会将第二个任务堵塞

    若对象不是同一个,则不会发生堵塞

    //调用二个不同的对象
    Thread thread_one = new Thread(new Test(), "Thread_ONE");
    Thread thread_two = new Thread(new Test(), "Thread_Two");
    thread_one.start();
    thread_two.start();

    结果是

    Thread_Two 2
    Thread_ONE 1
    Thread_Two 3
    Thread_ONE 4
    Thread_Two 5
    Thread_ONE 6
    Thread_Two 7
    Thread_ONE 8
    Thread_Two 9
    Thread_ONE 10

     

  2.  当修饰的方法为静态的方法时,作用的范围是一个类,而非一个对象。

    不同线程的相同类调用该方法时都会发生堵塞

    //调用相同的对象
    Test test = new Test();
    Thread thread_one = new Thread( test, "Thread_ONE" );
    Thread thread_two = new Thread( test, "Thread_Two" );
    thread_one.start();
    thread_two.start();
    
    //调用二个不同的对象
    Thread thread_one = new Thread(new Test(), "Thread_ONE");
    Thread thread_two = new Thread(new Test(), "Thread_Two");
    thread_one.start();
    thread_two.start();

    结果都为

    Thread_ONE 1
    Thread_ONE 2
    Thread_ONE 3
    Thread_ONE 4
    Thread_ONE 5
    Thread_Two 6
    Thread_Two 7
    Thread_Two 8
    Thread_Two 9
    Thread_Two 10

 

当 synchronized 修饰一个代码块时,作用的范围看括号内的内容。若括号内为对象,则范围是一个对象;若括号内为类,则范围是一个类

  1. 若括号内为对象,则范围是一个对象,效果和 synchronized 修饰非静态方法一样

    public void runTest() {
        synchronized (this) { // 括号内为一个对象
            for (int i = 0; i < 5; ++i) {
                Count++;
                System.out.println(Thread.currentThread().getName() + " " + Count);
            }
        }
    }

    效果

    //调用相同的对象
    Test test = new Test();
    Thread thread_one = new Thread( test, "Thread_ONE" );
    Thread thread_two = new Thread( test, "Thread_Two" );
    thread_one.start();
    thread_two.start();
    //发生类堵塞
    //Thread_ONE 1
    //Thread_ONE 2
    //Thread_ONE 3
    //Thread_ONE 4
    //Thread_ONE 5
    //Thread_Two 6
    //Thread_Two 7
    //Thread_Two 8
    //Thread_Two 9
    //Thread_Two 10
    
    
    //调用二个不同的对象
    Thread thread_one = new Thread(new Test(), "Thread_ONE");
    Thread thread_two = new Thread(new Test(), "Thread_Two");
    thread_one.start();
    thread_two.start();
    // 没有发生堵塞
    //Thread_Two 2
    //Thread_ONE 1
    //Thread_Two 3
    //Thread_ONE 4
    //Thread_Two 5
    //Thread_ONE 6
    //Thread_Two 7
    //Thread_ONE 8
    //Thread_Two 9
    //Thread_ONE 10
  2.  若括号内为类,则范围是一个对象,效果和 synchronized 修饰静态方法一样

    public [static] void runTest() {  
        synchronized (Test.class) { // 括号内为一个类
            for (int i = 0; i < 5; ++i) {
                Count++;
                System.out.println(Thread.currentThread().getName() + " " + Count);
            }
        }
    }

    二种调用都发生堵塞 

    Thread_ONE 1
    Thread_ONE 2
    Thread_ONE 3
    Thread_ONE 4
    Thread_ONE 5
    Thread_Two 6
    Thread_Two 7
    Thread_Two 8
    Thread_Two 9
    Thread_Two 10

 

要注意的一点是,以上的例子不同的线程都是调用同一段的代码,有可能会出现误点,认为一段代码块对应一把锁。

synchronized的锁锁住的是对象,或者某个类。一个对象或者一个类对应一把锁。

class Example {
    public synchronized void printfA()
    {
        //synchronized (this) {
            for(int i = 1; i <= 100; ++i)
                System.out.println("A "+i);
        //}
    }
    public synchronized void printfB()
    {
        //synchronized (this) {
            for(int i = 1; i <= 100; ++i)
                System.out.println("B "+i);
        //}
    }
}
public class test{
    Example ex = new Example();
    RunA runa = new RunA();
    RunB runb = new RunB();
    class  RunA implements Runnable {
        @Override
        public void run() {
            ex.printfA();
        }
    }
    class  RunB implements Runnable {
        @Override
        public void run() {
            ex.printfB();
        }
    }
    public static void main(String[] args)
    {  
        test t = new test();
        Thread A = new Thread( t.runa );
        Thread B = new Thread( t.runb );
        A.start(); B.start();
    }
}

注意这个例子,当线程 A 调用了 printfA() 方法时,获得了对象 ex 的锁,而线程 B 调用了 printfB()方法,这里虽然和线程 A 调用的方法不一样,但是锁是同一个,都是 ex 的锁,所以锁已经被线程 A 获得,线程 B 将被堵塞,只是等线程 A 释放锁(运行完printfA())线程B才会运行。(有可能线程 B 比线程 A 先获得锁),所以 A 和 B 的输出不会出现交叉。

 

小结:多线程中 锁 的最主要的目的就是为了确保多线程间的安全性,同一时间只用一个线程对相同一处内存进行操作,而程序运行的本质之一也就是对内存的操作。静态类在编译的时候就分配类内存,锁的对象要是是静态的,那操作都是同一个内存,作用对象也是这个类;锁的对象要是非静态的,也就是是一个对象,那在对象实例化的时候分配内存,不同的对象有着不同的内存。

 

参考:http://tutorials.jenkov.com/java-concurrency/synchronized.html

转载于:https://www.cnblogs.com/lkcc/p/8178963.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值