Java 线程同步之 synchronized

synchronized是一种同步锁,是Java中的关键字,其用法如下:

1 修饰一段代码块(锁定对象)
被synchronized修饰的代码块称之为同步代码块,写法如下,这里锁定的是对象mBook。

synchronized (mBook) {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

在Java中,每一个对象都拥有一个锁标记(monitor),也称为监视器,多线程同时访问某个对象时,线程只有获取了该对象的锁才能访问。在同步代码块中,锁定的就是一个对象,就是本例中的mBook。多个线程同时调用锁定mBook的代码块时,只有一个能获得mBook的对象锁,当该线程释放锁之后,另一个线程才能获取锁开始执行同步代码块。

2 修饰一个实例方法(锁定对象)
被synchronized修饰的方法称之为同步方法,修饰一个实例方法的写法如下:

public synchronized void setBookName(String bookName) {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    this.bookName = bookName;
}

修饰实例方法时,和同步代码块中一样,锁定的也是一个对象。线程A调用对象的同步方法时,需要获取对象锁,如果对象锁被另一个线程B持有,线程A会阻塞等待线程B释放锁对象。

3 修饰一个静态方法(锁定类)

synchronized可以修饰一个静态方法,如下:
public synchronized static int getCount() {
   mCount++;
   return mCount;
}

修饰静态方法时,锁定的不是一个对象,而是这个类。每个类有一个类锁,是用来控制对static成员的并发访问,它和对象锁不是一个东西。例如:线程A调用类T的静态方法getCount(),同时线程B也调用类T的静态方法getCount(),那么只有一个线程能获得T的类锁。

4 修饰一段代码块(锁定类)
synchronized修饰代码块时,也可以锁定类,写法如下:

synchronized (Book.class) {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
 }

这里锁定的是类,某个线程调用同步代码块时,获取的是类锁。

5 synchronized用法总结
使用synchronized,可以用来获取对象锁,或者类锁。
每个对象只有一个对象锁,当对象锁被某个线程持有时,其他尝试获得该对象锁的线程都会被阻塞。
每个类只有一个类锁,当类锁被某个线程持有时,其他尝试获得该类锁的线程都会被阻塞。
对象锁和类锁不是一个东西,类锁用来控制对static成员的并发访问。例如:类T有一个实例对象T1,线程A持有对象T1的对象锁时,线程B尝试获取类T的类锁,线程B可以成功获取类锁。线程A和B可以并发执行。

6 synchronized使用实例
定义类Book,声明并实现同步方法,一个为实例同步方法,一个为静态同步方法。

public class Book {

    private final String TAG = this.getClass().getSimpleName();

    private String bookName;
    private static int mCount;

    Book(String name) {
        bookName = name;
    }

    public String getBookName() {
        return bookName;
    }

    public synchronized void setBookName(String bookName) {
        Log.e(TAG, "setting book name begin");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.bookName = bookName;
        Log.e(TAG, "setting book name ends");
    }

    public synchronized static int getCount() {
        Log.e("Book", "getting count begin");

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mCount++;
        Log.e("Book", "getting count end");
        return mCount;
    }
}

下面是测试类:

public class SynchronizedTest {

    private final String TAG = this.getClass().getSimpleName();

    private Book mBook;

    public void start() {
        mBook = new Book("Little Prince");

        //对象锁测试
        Task1 task1 = new Task1(1);
        Thread thread1 = new Thread(task1);
        thread1.start();

        Task2 task2 = new Task2(2);
        Thread thread2 = new Thread(task2);
        thread2.start();

        Task3 task3 = new Task3(3);
        Thread thread3 = new Thread(task3);
        thread3.start();

        //类锁测试
        Task4 task4 = new Task4(4);
        Thread thread4 = new Thread(task4);
        thread4.start();

        Task5 task5 = new Task5(5);
        Thread thread5 = new Thread(task5);
        thread5.start();
    }

    public class Task1 implements Runnable {
        int taskId;
        byte[] lock = new byte[0];

        Task1(int id) {
            taskId = id;
        }

        @Override
        public void run() {
            synchronized (mBook) {
                Log.e(TAG, "task:" + taskId + " begin");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.e(TAG, "task:" + taskId + " end");
            }
        }
    }

    public class Task2 implements Runnable {
        int taskId;

        Task2(int id) {
            taskId = id;
        }

        @Override
        public void run() {
            synchronized (mBook) {
                Log.e(TAG, "task:" + taskId + " begin");
                Log.e(TAG, "task:" + taskId + " book name:" + mBook.getBookName());
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.e(TAG, "task:" + taskId + " end");
            }
        }
    }

    public class Task3 implements Runnable {
        int taskId;

        Task3(int id) {
            taskId = id;
        }

        @Override
        public void run() {
            //调用实例同步方法,如果该对象的锁被其他线程持有,则阻塞。
            mBook.setBookName("Beauty&Beast");
            //调用实例同步方法结束,释放锁。
            Log.e(TAG, "task:" + taskId + " book name:" + mBook.getBookName());
        }
    }

    public class Task4 implements Runnable {
        int taskId;

        Task4(int id) {
            taskId = id;
        }

        @Override
        public void run() {
            //调用静态同步方法,如果该类的锁被其他线程持有,则阻塞。
            int count = Book.getCount();
            //调用静态同步方法结束,释放锁。
            Log.e(TAG, "task:" + taskId + " book count:" + count);
        }
    }

    public class Task5 implements Runnable {
        int taskId;

        Task5(int id) {
            taskId = id;
        }

        @Override
        public void run() {
            synchronized (Book.class) {
                Log.e(TAG, "task:" + taskId + " begin");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.e(TAG, "task:" + taskId + " end");
            }
        }
    }
}

线程thread1、thread2、thread3执行任务时,需要获取的mBook的对象锁,所以一个线程释放对象锁之后,另一个线程才能获取对象锁并执行同步代码块或同步方法,否则一直阻塞。日志如下:
在这里插入图片描述
线程thread4、thread5需要获取的是Book类的类锁,这两个线程中的同步代码块或同步方法是线性执行的。日志如下:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值