我们知道在多线程环境下,我们常常需要给一些共享的东西加锁来保证安全性,在访问某个共享资源的时候,就需要获取到它锁,而这个这个锁就分为很多种,类锁、对象锁、引用锁。
结论
1.对于synchronized关键字,作用在方法上,如public (static) synchronized 返回值类型 方法名,如果是静态方法,那么此处的锁就是类锁,如果是普通方法,锁就是对象锁。
2.synchronized(this){…}就代表此处的锁就是对象锁,synchronized(类.class){…}就代表此处锁就是类锁。
3.对于引用锁,synchronized(非静态引用){…},实际上就代表此处的锁就是发挥着和对象锁一样的作用,synchronized(静态引用){…},实际上就代表此处的锁发挥着和类锁一样的作用。
4.一个实例只能有一个对象锁,那么在同一时刻,外部的线程最多只能访问一个被synchronized修饰的方法。
对象锁
所谓对象锁,从名字上看就是锁住对象的锁,对象就是实例,一个类可以有多个实例,每个实例都可以有自己的锁,或者这样说每个实例都都可以锁住属于自己的东西,那么一个类中什么是属于实例的的那,非静态的方法、变量、代码块、,那么作用在这些东西上的锁都可以是对象锁,需要注意的是在java中synchronized不能修饰变量,并且一个实例只能有一个锁对象,只能修饰方法和代码块。所以属于对象的,并且可以加锁的就是非静态方法和非静态代码块。对于方法的加锁方式 :public synchronized void say(){…},对于代码块的加锁方式synchronized(this){ …},代码如下:
/**
* @author 理想
* @version 1.0
* @date 2021/7/16 1:06
*/
public class Test01 {
public int a=5;
public static int count=0;
{
synchronized(this)
{
System.out.println( "这是第"+ ++count+"个实例");
}
}
public synchronized void say()
{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("你好啊"+ ++count);
}
public synchronized void eat()
{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("好好吃");
}
public void two()
{
synchronized (this)
{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是对象锁的另一种表现方式");
}
}
}
在主方法中进行测试
/**
* @author 理想
* @version 1.0
* @date 2021/7/16 1:17
*/
public class demo01 {
public static void main(String[] args) {
Test01 test01 = new Test01();
Test01 test02 = new Test01();
/**
* 当两个线程同时访问一个对象的时候,线程A获取到了锁,那么线程B就得等待,存在竞争关系
*/
// new Thread(new Runnable() {
// @Override
// public void run() {
// test01.say();
// }
// }).start();
// new Thread(new Runnable() {
// @Override
// public void run() {
// test01.say();
// }
// }).start();
/**
* 当两个线程去访问不同的对象的时候,获取的是不同的锁,不存在竞争关系
*/
// new Thread(new Runnable() {
// @Override
// public void run() {
// test01.say();
// }
// }).start();
// new Thread(new Runnable() {
// @Override
// public void run() {
// test02.say();
// }
// }).start();
/**
* 当两个线程同时访问同一个对象里面的不同加锁方法,由于当前的锁是以对象为锁,那么就存在竞争关系
* 比如说这里 非静态的的say和eat方法都加了synchronized,都是同一个对象的锁,要么say先执行,eat等待,要么执行eat的线程先抢到锁,执行say的
* 线程等待
*/
// new Thread(new Runnable() {
// @Override
// public void run() {
// test01.say();
// }
// }).start();
// new Thread(new Runnable() {
// @Override
// public void run() {
// test01.eat();
// }
// }).start();
/**
* 对象锁实际上也可以用synchronized (this)来,这样的效果实际上是一样的,只是写法不一样,同样也需要竞争
*/
new Thread(new Runnable() {
@Override
public void run() {
test01.two();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
test01.two();
}
}).start();
}
}
类锁
类锁的意思就是以当前类对象为锁,以类本身为锁,类锁就很有意思了,锁住自己的东西,静态方法,静态代码快,public static synchronized 返回值类型 方法名,这本身就可以是个类锁,就不用过多解释了,但是你也可以作用非静态代码块上,代码可以像下面这么写,但是我也不知道这么写的意义是什么。
public void sleep02()
{
synchronized (Test02.class)
{
…
}
}
这个方法还是属于每个对象的,但是当一个对象在使用的时候,其他对象却无法使用这个方法,不知道这种模式的应用在哪。
/**
* @author 理想
* @version 1.0
* @date 2021/7/16 1:41
*/
public class Test02 {
/**
* 第一种表现方式
*/
public static synchronized void sleep()
{
System.out.println("我是类的锁");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 作用在静态代码块
*/
static {
synchronized (Test02.class)
{
}
}
/**
* 第二种类锁的表现方式
*/
public void sleep02()
{
/**
* 作用在非静态代码快
*/
synchronized (Test02.class)
{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"来了");
}
}
}
在主方法中测试
/**
* @author 理想
* @version 1.0
* @date 2021/7/16 1:42
*/
public class demo02 {
public static void main(String[] args) {
Test02 test01 = new Test02();
Test02 test02 = new Test02();
/**
* 对于类锁而言,锁的是属于类的东西,如静态代码块,静态方法
*/
// new Thread(new Runnable() {
// @Override
// public void run() {
// Test02.sleep();
// }
// }).start();
// new Thread(new Runnable() {
// @Override
// public void run() {
// Test02.sleep();
// }
// }).start();
/**
* 使用类锁,锁住某个非静态方法中的代码块,这样的话,不同的线程去访问不同对象的同一方法也会竞争锁对象
*/
new Thread(new Runnable() {
@Override
public void run() {
test01.sleep02();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
test02.sleep02();
}
}).start();
}
}
引用锁
引用锁就是说在类中创建一个任意类型的引用,假如说你创建一个静态的,那么这个引用属于类本身,使用这个引用加锁,就相当于使用类加锁的效果,假如你创建一个非静态的引用,那么这个引用属于实例,每个实例都有,那么使用这个引用加锁,就相当于使用对象锁加锁的效果。测试代码如下
/**
* @author 理想
* @version 1.0
* @date 2021/7/16 2:05
*/
public class Test03 {
/**
* 引用锁
*/
//一个普通的
Object object=new Object();
//一个静态的
static Object object_static=new Object();
public void happy()
{
/**
* 这是一个普通的成员变量,与锁对象中的this起的作用差不多
*/
synchronized (object)
{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"非静态变量");
}
}
public void mad()
{
/***
* 静态变量,属于类,与 类.class差不多
*/
synchronized (object_static)
{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"静态变量");
}
}
}
测试类如下
/**
* @author 理想
* @version 1.0
* @date 2021/7/16 2:05
*/
public class demo03 {
public static void main(String[] args) {
Test03 test01 = new Test03();
Test03 test02 = new Test03();
/**
* 两个线程去访问两个对象的被非静态引用锁加锁的happy方法,不存在竞争关系
*/
// new Thread(new Runnable() {
// @Override
// public void run() {
// test01.happy();
// }
// }).start();
// new Thread(new Runnable() {
// @Override
// public void run() {
// test02.happy();
// }
// }).start();
/**
* 两个线程都去访问被静态引用锁加锁的mad,竞争一个锁,存在竞争关系
*/
new Thread(new Runnable() {
@Override
public void run() {
test01.mad();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
test02.mad();
}
}).start();
}
}
在本中的代码都是根据代码的输出先后,间隔时间来判断是否产生了竞争
这就是我对几种加锁的理解,如有错误,欢迎指出,感激不尽 !