11.线程八锁
线程八锁
• 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
• 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
• 加个普通方法后发现和同步锁无关
• 换成两个对象后,不是同一把锁了,情况立刻变化。
• 都换成静态同步方法后,情况又变化
• 所有的非静态同步方法用的都是同一把锁——实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
• 所有的静态同步方法用的也是同一把锁——类对象本身,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
代码演示
1. 两个普通同步方法,两个线程,标准打印 数字,确认打印的顺序
1.1 首先写一个打印数字的方法类,两个方法,各自可以打印 one , two
//编写专门打印数字的方法类
class Number{
//打印 one:注意设置同步方法 synchronized
public synchronized void getOne(){
// 打印 one
System.out.println("one");
}
//打印 two:注意设置同步方法 synchronized
public synchronized void getTwo(){
//打印two
System.out.println("two");
}
}
1.2 创建两个线程,确认打印数字的顺序
public class TestThread8Monitor {
public static void main(String[] args) {
test01();
}
//1. 两个普通同步方法,两个线程,标准打印 数字,确认打印的顺序
public static void test01(){
//1.创建1个Number类对象,此时两个同步方法当前的锁为一个number对象
Number number = new Number();
//2.开启两个线程,因为当前两个线程的锁都是同一个number对象
// 所以打印的信息,应该是 one two
new Thread(new Runnable() {
@Override
public void run() {
number.getOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
number.getTwo();
}
}).start();
}
}
测试执行如下:
2. 新增 Thread.sleep() 给 getOne(), 确认两个线程的打印顺序
2.1 在 getOne() 方法 设置 休眠
测试执行如下:
因为此时两个线程的锁都是同一个 number 对象,所以不管线程是否设置休眠,都是按照顺序同步执行的。
2.2 测试代码
public class TestThread8Monitor {
public static void main(String[] args) {
test01();
}
//1. 两个普通同步方法,两个线程,标准打印 数字,确认打印的顺序
public static void test01(){
//1.创建1个Number类对象,此时两个同步方法当前的锁为一个number对象
Number number = new Number();
//2.开启两个线程,因为当前两个线程的锁都是同一个number对象
// 所以打印的信息,应该是 one two
new Thread(new Runnable() {
@Override
public void run() {
number.getOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
number.getTwo();
}
}).start();
}
}
//编写专门打印数字的方法类
class Number{
//打印 one:注意设置同步方法 synchronized
public synchronized void getOne(){
// 休眠3秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印 one
System.out.println("one");
}
//打印 two:注意设置同步方法 synchronized
public synchronized void getTwo(){
System.out.println("two");
}
}
3. 新增普通方法 getThree(), 并且建立线程。查看打印顺序
3.1 创建 getThree() 的普通方法,注意不是同步方法,也就是说跟 getOne() getTwo() 不是共用一个锁。
//编写专门打印数字的方法类
class Number{
//打印 one:注意设置同步方法 synchronized
public synchronized void getOne(){
// 休眠3秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印 one
System.out.println("one");
}
//打印 two:注意设置同步方法 synchronized
public synchronized void getTwo(){
System.out.println("two");
}
//打印 three。普通方法,没有设置同步锁
public void getThree(){
System.out.println("three");
}
}
3.2 在测试代码增加执行 getThree() 的线程,查看打印的顺序
//1.创建1个Number类对象,此时两个同步方法当前的锁为一个number对象
Number number = new Number();
//2.开启两个线程,因为当前两个线程的锁都是同一个number对象
// 所以打印的信息,应该是 one two
new Thread(new Runnable() {
@Override
public void run() {
number.getOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
number.getTwo();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
number.getThree(); // 因为没有同步锁,所以会优先并发打印【因为getOne需要修改3秒】
}
}).start();
3.3 分析
“
首先 getThree 没有同步锁,所以是并发执行的。
而 getOne 应该也是并发执行的,但是需要休眠3秒后才 打印。
而 getTwo 因为与 getOne 共用一个同步锁 number 对象,所以需要等待 getOne 执行完毕之后,才能执行 getTwo
”
4. 两个普通同步方法,两个 Number 对象 ,查看打印顺序
4.1 保持 getOne 与 getTwo 两个同步方法
//编写专门打印数字的方法类
class Number{
//打印 one:注意设置同步方法 synchronized
public synchronized void getOne(){
// 休眠3秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印 one
System.out.println("one");
}
//打印 two:注意设置同步方法 synchronized
public synchronized void getTwo(){
System.out.println("two");
}
}
4.2 创建两个 Number 对象,分别用不同的 Number 对象调用 getOne 和 getTwo 执行线程, 查看打印的顺序
//1.创建2个Number类对象,
// 此时两个同步方法当前的锁为各自的number1和number2对象,并不共用锁,也就是可以并发
Number number1 = new Number();
Number number2 = new Number();
//2.开启两个线程
// 此时两个同步方法当前的锁为各自的number1和number2对象,并不共用锁,也就是可以并发
// 虽然并发,但是getOne需要休眠3秒,所以优先打印getTwo,结果:two one
new Thread(new Runnable() {
@Override
public void run() {
number1.getOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
number2.getTwo();
}
}).start();
5. 修改 getOne() 为静态同步方法,保持 getTwo() 为普通的同步方法,查看打印的顺序
5.1 修改 getOne 为静态同步方法
class Number{
//打印 one:注意设置同步方法 synchronized
public static synchronized void getOne(){
// 休眠3秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印 one
System.out.println("one");
}
//打印 two:注意设置同步方法 synchronized
public synchronized void getTwo(){
System.out.println("two");
}
}
5.3 测试方法
//1.创建1个Number类对象,
Number number = new Number();
//2.开启两个线程
// 此时getOne的同步锁是 Number.class,而getTwo的同步锁是 number 对象
// 也就是说两个线程不是共用一个同步锁,所以两个线程可以并发
// 其中getOne需要休眠3秒,才打印信息。所以打印结果: two one
new Thread(new Runnable() {
@Override
public void run() {
Number.getOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
number.getTwo();
}
}).start();
6. 修改两个方法均为静态同步方法,一个 Number 对象
6.1 设置两个方法为静态同步方法
//编写专门打印数字的方法类
class Number{
//打印 one:注意设置同步方法 synchronized
public static synchronized void getOne(){
// 休眠3秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印 one
System.out.println("one");
}
//打印 two:注意设置同步方法 synchronized
public static synchronized void getTwo(){
System.out.println("two");
}
}
6.2 测试方法
//1.创建1个Number类对象,
Number number = new Number();
//2.开启两个线程
// 此时getOne 和 getTwo的同步锁是 number 对象的 Class
// 也就是说两个线程共用一个同步锁,所以两个线程串行同步执行
// 所以打印结果: one two
new Thread(new Runnable() {
@Override
public void run() {
number.getOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
number.getTwo();
}
}).start();
7.一个静态同步方法,一个非静态同步方法,两个 Number 对象
7.1 修改getOne 和 getTwo 方法
//编写专门打印数字的方法类
class Number{
//打印 one:注意设置同步方法 synchronized
public static synchronized void getOne(){
// 休眠3秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印 one
System.out.println("one");
}
//打印 two:注意设置同步方法 synchronized
public synchronized void getTwo(){
System.out.println("two");
}
}
7.2 测试方法
//1.创建2个Number类对象,
Number number1 = new Number();
Number number2 = new Number();
//2.开启两个线程
// 此时getOne 和 getTwo的同步锁是不同的,各自是number1和number2对象
// 所以两个线程可以并发执行。由于getOne需要休眠3秒,
// 所以打印结果: two one
new Thread(new Runnable() {
@Override
public void run() {
number1.getOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
number2.getTwo();
}
}).start();
8.两个静态同步方法,两个 Number 对象
8.1 设置两个静态同步方法
//编写专门打印数字的方法类
class Number{
//打印 one:注意设置同步方法 synchronized
public static synchronized void getOne(){
// 休眠3秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印 one
System.out.println("one");
}
//打印 two:注意设置同步方法 synchronized
public static synchronized void getTwo(){
System.out.println("two");
}
}
8.2 测试方法
//1.创建2个Number类对象,
Number number1 = new Number();
Number number2 = new Number();
//2.开启两个线程
// 此时getOne 和 getTwo的同步锁相同的,都是 Number.class
// 所以两个线程同步串行执行。
// 所以打印结果: one two
new Thread(new Runnable() {
@Override
public void run() {
number1.getOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
number2.getTwo();
}
}).start();
总结
/*
* 线程八锁的关键:
* ①非静态方法的锁默认为 this, 静态方法的锁为 对应的 Class 实例
* ②某一个时刻内,只能有一个线程持有锁,无论几个方法。
*/
原文始发于微信公众号(海洋的渔夫):11.线程八锁
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/34933.html