目录
面试题
案例 1 标准访问有 ab 两个线程
案例 2 其中一个同步方法暂停 3 秒
案例 3 新增普通方法
案例 4 创建两个对象
案例 5 两个静态同步方法 一个对象
案例 6 两个静态同步方法 两个对象
案例 7 一个静态同步方法 一个普通同步方法 一个对象
案例 8 一个静态同步方法 一个普通同步方法 两个对象
思考总结
notify 方法
面试题
案例 1 标准访问有 ab 两个线程
在主线程里面暂停几秒钟的线程
import java.util.concurrent.TimeUnit;
// 资源类
class phone{
public synchronized void sendEmail(){
System.out.println("---email---");
}
public synchronized void sendSMS(){
System.out.println("---sms---");
}
}
public class LockDemo1 {
public static void main(String[] args) { // 主线程
phone phone = new phone(); // 资源类
new Thread(()->{
phone.sendEmail();
},"a").start();
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.sendSMS();
},"b").start();
}
}
打印输出
案例 2 其中一个同步方法暂停 3 秒
在资源类的方法里面暂停几秒钟
import java.util.concurrent.TimeUnit;
// 资源类
class phone{
public synchronized void sendEmail(){
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("---email---");
}
public synchronized void sendSMS(){
System.out.println("---sms---");
}
}
public class LockDemo1 {
public static void main(String[] args) { // 主线程
phone phone = new phone(); // 资源类
new Thread(()->{
phone.sendEmail();
},"a").start();
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.sendSMS();
},"b").start();
}
}
sendEmail()
和 sendSMS()
都是使用 synchronized
修饰的,这意味着它们在同一时刻只能有一个线程能进入执行。加锁是基于 phone
类的实例的,也就是说,如果有一个线程在执行某个同步方法,其他线程就会被阻塞,直到该线程执行完成并释放锁。
- 当第一个线程(线程
a
)执行phone.sendEmail()
方法时,它会获取phone
对象的锁,并且由于该方法包含TimeUnit.SECONDS.sleep(2)
,它会在执行时暂停 2 秒。 - 在
sendEmail
方法执行期间,第二个线程(线程b
)尝试执行phone.sendSMS()
,但是由于sendSMS
也是一个同步方法,且它也是基于phone
对象的锁,所以它会被阻塞,直到线程a
执行完成并释放锁。
案例 3 新增普通方法
import java.util.concurrent.TimeUnit;
// 资源类
class phone{
public synchronized void sendEmail(){
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("---email---");
}
public synchronized void sendSMS(){
System.out.println("---sms---");
}
public void hello(){
System.out.println("---hello---");
}
}
public class LockDemo {
public static void main(String[] args) { // 主线程
phone phone = new phone(); // 资源类
new Thread(()->{
phone.sendEmail();
},"a").start();
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.hello();
phone.sendSMS();
},"b").start();
}
}
案例 4 创建两个对象
对象锁
一个对象一把锁
两个对象两把锁
import java.util.concurrent.TimeUnit;
// 资源类
class phone{
public synchronized void sendEmail(){
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("---email---");
}
public synchronized void sendSMS(){
System.out.println("---sms---");
}
public void hello(){
System.out.println("---hello---");
}
}
public class LockDemo {
public static void main(String[] args) { // 主线程
phone phone = new phone(); // 资源类
phone phone2 = new phone(); // 资源类
new Thread(()->{
phone.sendEmail();
},"a").start();
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
// phone.hello();
// phone.sendSMS();
phone2.sendSMS();
},"b").start();
}
}
案例 5 两个静态同步方法 一个对象
静态关键字
import java.util.concurrent.TimeUnit;
// 资源类
class phone{
public static synchronized void sendEmail(){
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("---email---");
}
public static synchronized void sendSMS(){
System.out.println("---sms---");
}
public void hello(){
System.out.println("---hello---");
}
}
public class LockDemo {
public static void main(String[] args) { // 主线程
phone phone = new phone(); // 资源类
new Thread(()->{
phone.sendEmail();
},"a").start();
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.sendSMS();
},"b").start();
}
}
当你在 sendEmail()
和 sendSMS()
方法上添加了 static synchronized
关键字后,锁的行为发生了变化。原来这些方法是基于实例对象 (phone
类的实例) 的锁,而加了 static
之后,这些方法变成了基于类锁(类对象锁)。
关键点:
- 非静态同步方法:如果没有
static
,同步方法会使用实例对象的锁(即phone
类的实例锁),每个对象都有自己的锁。
-
- 在这种情况下,两个线程在同一个
phone
实例上调用sendEmail()
和sendSMS()
时,锁是独立的,因为每个对象的锁是分开的。
- 在这种情况下,两个线程在同一个
- 静态同步方法:当你使用
static synchronized
时,锁就变成了类级别的锁。这意味着所有类的实例(无论创建多少个实例)都会共享同一把锁,而这把锁是与phone
类相关的,而不是某个具体实例对象。
-
- 也就是说,
sendEmail()
和sendSMS()
不再是基于单个对象phone
的锁,而是基于phone
类的类锁。
- 也就是说,
对你的代码的影响:
- 静态同步方法:
sendEmail()
和sendSMS()
都是静态同步方法,这意味着它们都使用phone.class
作为锁。
-
- 当线程
a
执行phone.sendEmail()
时,它会获取phone.class
锁,阻塞其他线程执行任何静态同步方法,直到sendEmail()
完成。 - 当线程
b
执行phone.sendSMS()
时,它也需要获取phone.class
锁,但因为线程a
正在执行sendEmail()
,它会被阻塞直到sendEmail()
执行完成。
- 当线程
- 非静态方法:
hello()
方法没有synchronized
和static
,因此它不是同步的,线程可以自由地调用它,且不会与静态同步方法互相影响。
锁的流程:
- 线程
a
调用sendEmail()
,获取phone.class
锁,开始执行,执行期间睡眠 3 秒。 - 线程
b
调用sendSMS()
,但是由于sendEmail()
还在执行,sendSMS()
会被阻塞,直到线程a
执行完sendEmail()
并释放phone.class
锁。
总结:
当你在方法上加了 static synchronized
关键字时,锁是基于类锁(phone.class
),而不是基于对象锁(phone
实例)。这意味着所有线程都必须获取类级别的锁才能进入这两个方法。所以,即使你创建了多个 phone
类的实例,静态同步方法依然会使用同一个类锁。
案例 6 两个静态同步方法 两个对象
import java.util.concurrent.TimeUnit;
// 资源类
class phone{
public static synchronized void sendEmail(){
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("---email---");
}
public static synchronized void sendSMS(){
System.out.println("---sms---");
}
public void hello(){
System.out.println("---hello---");
}
}
public class LockDemo {
public static void main(String[] args) { // 主线程
phone phone = new phone(); // 资源类
phone phone2 = new phone(); // 资源类
new Thread(()->{
phone.sendEmail();
},"a").start();
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.sendSMS();
},"b").start();
}
}
案例 7 一个静态同步方法 一个普通同步方法 一个对象
import java.util.concurrent.TimeUnit;
// 资源类
class phone{
public static synchronized void sendEmail(){
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("---email---");
}
public synchronized void sendSMS(){
System.out.println("---sms---");
}
public void hello(){
System.out.println("---hello---");
}
}
public class LockDemo {
public static void main(String[] args) { // 主线程
phone phone = new phone(); // 资源类
phone phone2 = new phone(); // 资源类
new Thread(()->{
phone.sendEmail();
},"a").start();
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.sendSMS();
},"b").start();
}
}
案例 8 一个静态同步方法 一个普通同步方法 两个对象
import java.util.concurrent.TimeUnit;
// 资源类
class phone{
public static synchronized void sendEmail(){
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("---email---");
}
public synchronized void sendSMS(){
System.out.println("---sms---");
}
public void hello(){
System.out.println("---hello---");
}
}
public class LockDemo {
public static void main(String[] args) { // 主线程
phone phone = new phone(); // 资源类
phone phone2 = new phone(); // 资源类
new Thread(()->{
phone.sendEmail();
},"a").start();
// 暂停几秒钟的线程
try{
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.sendSMS();
},"b").start();
}
}
思考总结
我们要明白 sync 锁定的是什么
阿里开发手册
能用对象锁 就不要用类锁
synchronized 不是锁方法
是锁资源类
同步方法资源类是对象
同步静态方法资源类是当前类
同步方法锁的是当前对象
同一时间只能允许一个 线程进来同步方法
不允许同时
普通的同步方法锁的是对象锁 对象.class
静态的同步方法锁的是模版 类锁 类.class
能用对象锁 就不要用类锁