synchronized内置锁
每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。
内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,知道线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。
synchronized的使用
在学习使用synchronized前,我们先来看一段代码:
public class SyncTest {
private long count = 0;
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
public void incCount() {
count++;
}
//线程
private static class Count extends Thread {
private SyncTest simplOper;
public Count(SyncTest simplOper) {
this.simplOper = simplOper;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
simplOper.incCount();//count = count+10000
}
}
}
public static void main(String[] args) throws InterruptedException {
SyncTest simplOper = new SyncTest();
//启动两个线程
Count count1 = new Count(simplOper);
Count count2 = new Count(simplOper);
count1.start();
count2.start();
Thread.sleep(50);
System.out.println(simplOper.count);
}
}
开启了2个线程,每个线程都累加了10000次,如果结果正确的话自然而然总数就应该是20000。可就运行多次结果都不是这个数,而且每次运行结果都不一样。使用synchronized内置锁就可以解决上面的问题。
作用在方法上
public class SyncTest {
private long count = 0;
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
//使用synchronized作用在方法上
public synchronized void incCount() {
count++;
}
//线程
private static class Count extends Thread {
private SyncTest simplOper;
public Count(SyncTest simplOper) {
this.simplOper = simplOper;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
simplOper.incCount();//count = count+10000
}
}
}
public static void main(String[] args) throws InterruptedException {
SyncTest simplOper = new SyncTest();
//启动两个线程
Count count1 = new Count(simplOper);
Count count2 = new Count(simplOper);
count1.start();
count2.start();
Thread.sleep(50);
System.out.println(simplOper.count);
}
}
打印结果
20000
作用在代码块上
public class SyncTest {
private long count = 0;
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
//使用synchronized作用在代码块上
public void incCount() {
synchronized(this){
count++;
}
}
//线程
private static class Count extends Thread {
private SyncTest simplOper;
public Count(SyncTest simplOper) {
this.simplOper = simplOper;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
simplOper.incCount();//count = count+10000
}
}
}
public static void main(String[] args) throws InterruptedException {
SyncTest simplOper = new SyncTest();
//启动两个线程
Count count1 = new Count(simplOper);
Count count2 = new Count(simplOper);
count1.start();
count2.start();
Thread.sleep(50);
System.out.println(simplOper.count);
}
}
对象锁和类锁
对象锁是用于对象实例方法,或者一个对象实例上的。类锁是用于类的静态方法或者一个类的 class 对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个 class 对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。
但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,类锁其实锁的是每个类的对应的 class 对象。类锁和对象锁之间也是互不干扰的。
对象锁
对象锁是用于对象实例方法,或者一个对象实例上
/**
* 类说明:锁的实例不一样,也是可以并行的
*/
public class DiffInstance {
private static class A implements Runnable {
private DiffInstance diffInstance;
public A(DiffInstance diffInstance) {
this.diffInstance = diffInstance;
}
@Override
public void run() {
System.out.println("A is running..." + diffInstance.toString());
diffInstance.instanceA();
}
}
private static class B implements Runnable {
private DiffInstance diffInstance;
public B(DiffInstance diffInstance) {
this.diffInstance = diffInstance;
}
@Override
public void run() {
System.out.println("B is running..." + diffInstance);
diffInstance.instanceB();
}
}
//作用于方法
private synchronized void instanceA() {
second(3);
System.out.println("A is going..." + this.toString());
second(3);
System.out.println("A ended " + this.toString());
}
//作用于对象实例
private synchronized void instanceB() {
synchronized (this){
second(3);
System.out.println("B is going..." + this.toString());
second(3);
System.out.println("B ended " + this.toString());
}
}
/**
* 按秒休眠
* @param seconds 秒数
*/
public static final void second(int seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
}
}
public static void main(String[] args) {
DiffInstance instance1 = new DiffInstance();
Thread t1 = new Thread(new B(instance1));
DiffInstance instance2 = new DiffInstance();
Thread t2 = new Thread(new A(instance2));
t1.start();
t2.start();
}
}
其中一次打印如下:
A is running...cn.wyhcsl.syn.DiffInstance@14e1774
B is running...cn.wyhcsl.syn.DiffInstance@469add4e
A is going...cn.wyhcsl.syn.DiffInstance@14e1774
B is going...cn.wyhcsl.syn.DiffInstance@469add4e
B ended cn.wyhcsl.syn.DiffInstance@469add4e
A ended cn.wyhcsl.syn.DiffInstance@14e1774
上面是执行结果,我们可以看到,结果输出是交替着进行输出的。说明锁的实例不一样,是可以并行的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b4GkeneR-1593620304436)(http://img.wyhcsl.cn//blog/20200701220302057.png)]
如果把上面main方法中使用同一个对象
public static void main(String[] args) {
DiffInstance instance1 = new DiffInstance();
Thread t1 = new Thread(new B(instance1));
//DiffInstance instance2 = new DiffInstance();
Thread t2 = new Thread(new A(instance1));
t1.start();
t2.start();
second(1);
}
其中一次打印如下:
A is running...cn.wyhcsl.syn.DiffInstance@3f455d86
B is running...cn.wyhcsl.syn.DiffInstance@3f455d86
A is going...cn.wyhcsl.syn.DiffInstance@3f455d86
A ended cn.wyhcsl.syn.DiffInstance@3f455d86
B is going...cn.wyhcsl.syn.DiffInstance@3f455d86
B ended cn.wyhcsl.syn.DiffInstance@3f455d86
上面是执行结果,我们可以看到一定要等某一个线程跑完之后,下一个才会接着执行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0H9AUZ6V-1593620304441)(http://img.wyhcsl.cn//blog/20200701220430134.png)]
也就是说,我们在执行某段代码的时候,要拿到这个对象上的锁才可以进行,当有2个对象的时候,对这2个线程来说是可以并行的。也充分的说明锁的实例不一样,是可以并行的。
类锁
类锁是作用于类的静态方法或者一个类的class对象上
public class DiffInstance {
private static class A implements Runnable {
private DiffInstance diffInstance;
public A(DiffInstance diffInstance) {
this.diffInstance = diffInstance;
}
@Override
public void run() {
System.out.println("A is running..." + diffInstance.toString());
diffInstance.instanceA();
}
}
private static class B implements Runnable {
private DiffInstance diffInstance;
public B(DiffInstance diffInstance) {
this.diffInstance = diffInstance;
}
@Override
public void run() {
System.out.println("B is running..." + diffInstance);
diffInstance.instanceB();
}
}
//作用于类的静态方法
private static synchronized void instanceA() {
second(1);
System.out.println("A is going...");
second(1);
System.out.println("A ended ");
}
//作用于类的class对象
private synchronized void instanceB() {
synchronized (DiffInstance.class){
second(1);
System.out.println("B is going..." + this.toString());
second(1);
System.out.println("B ended " + this.toString());
}
}
/**
* 按秒休眠
* @param seconds 秒数
*/
public static final void second(int seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
}
}
public static void main(String[] args) {
DiffInstance instance1 = new DiffInstance();
Thread t1 = new Thread(new B(instance1));
DiffInstance instance2 = new DiffInstance();
Thread t2 = new Thread(new A(instance2));
t1.start();
t2.start();
}
}
打印如下:
B is running...cn.wyhcsl.syn.DiffInstance@3abbf4ae
A is running...cn.wyhcsl.syn.DiffInstance@1667513b
B is going...cn.wyhcsl.syn.DiffInstance@3abbf4ae
B ended cn.wyhcsl.syn.DiffInstance@3abbf4ae
A is going...
A ended
区别
实例锁和类锁是不同的,两者可以并行。代码如下:
/**
*类说明:演示实例锁和类锁是不同的,两者可以并行
*/
public class InstanceAndClass {
private static class SynClass extends Thread{
@Override
public void run() {
System.out.println("TestClass is running...");
synClass();
}
}
private static class InstanceSyn implements Runnable{
private InstanceAndClass SynClassAndInstance;
public InstanceSyn(InstanceAndClass SynClassAndInstance) {
this.SynClassAndInstance = SynClassAndInstance;
}
@Override
public void run() {
System.out.println("TestInstance is running..."+SynClassAndInstance);
SynClassAndInstance.instance();
}
}
private synchronized void instance(){
SleepTools.second(1);
System.out.println("synInstance is going..."+this.toString());
SleepTools.second(1);
System.out.println("synInstance ended "+this.toString());
}
private static synchronized void synClass(){
SleepTools.second(1);
System.out.println("synClass going...");
SleepTools.second(1);
System.out.println("synClass end");
}
public static void main(String[] args) {
InstanceAndClass synClassAndInstance = new InstanceAndClass();
Thread t1 = new SynClass();
Thread t2 = new Thread(new InstanceSyn(synClassAndInstance));
t2.start();
SleepTools.second(1);
t1.start();
}
}
打印如下:
TestInstance is running...cn.enjoyedu.ch1.syn.InstanceAndClass@245c3ee2
synInstance is going...cn.enjoyedu.ch1.syn.InstanceAndClass@245c3ee2
TestClass is running...
synInstance ended cn.enjoyedu.ch1.syn.InstanceAndClass@245c3ee2
synClass going...
synClass end
synchronized作用于静态方法和非静态方法的区别:
-
非静态方法:给对象加锁(可以理解为给这个对象的内存上锁,注意 只是这块内存,其他同类对象都会有各自的内存锁),这时候在其他一个以上线程中执行该对象的这个同步方法(注意:是该对象)就会产生互斥
-
静态方法:相当于在类上加锁(*.class 位于代码区,静态方法位于静态区域,这个类产生的对象公用这个静态方法,所以这块内存,N个对象来竞争), 这时候,只要是这个类产生的对象,在调用这个静态方法时都会产生互斥