目录
问题描述
1 锁方法
2 锁代码块
3 锁某个类
4 静态方法上的synchronized
当我们处理多线程处理同步问题的时候就会用到synchronized这个关键字,下面介绍下synchronized的四种用法。
介绍之前我们先来看下,在java 多线程中 如果没有线程同步会出现什么问题:
下面这个是一个测试例子:
public class MainClass {
public static class MyRun implements Runnable
{
private int count=0;
@Override
public void run() {
while (count<15)
{
System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");
count++;
try {
Thread.sleep(200);
}
catch (Exception e)
{
}
}
}
}
public static void main(String args[])
{
MyRun myRun=new MyRun();
Thread threadA=new Thread(myRun,"A");
Thread threadB=new Thread(myRun,"B");
threadA.start();
threadB.start();
}
}
运行结果:
ThreadName:A count:0
ThreadName:B count:0
ThreadName:A count:1
ThreadName:A count:3
ThreadName:B count:2
ThreadName:A count:4
ThreadName:A count:6
ThreadName:A count:7
ThreadName:B count:5
我们看到这个count在无序的增加,这个是由于A,B两个线程同时操作Count变量造成的,如果我们想让Count有序增加,应该给
System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");
count++;
这段代码同步枷锁,这样当B线程进入这里时候,发现这里已经被锁,就只有等待,A执行完这段代码之后就会释放对这个对象的这段代码的释放锁,A获得了释放锁,就可以进入执行,让A,B有序进入执行,才能让Count有序增加,加入了synchronized之后的代码:
while (count<15) {
synchronized (this)
{
System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");
count++;
}
try {
Thread.sleep(200);
}
catch (Exception e)
{
}
}
结果:
ThreadName:A count:0
ThreadName:B count:1
ThreadName:B count:2
ThreadName:A count:3
ThreadName:A count:4
ThreadName:B count:5
ThreadName:B count:6
ThreadName:A count:7
ThreadName:B count:8
加了锁之后A,B线程就可以有序的交替执行,不会同时抢占执行Count++ 操作,
下面介绍synchronised的几种用法。
public synchronized void dodo(){ }
这个就是锁方法,这里面要注意两点:
synchronized 关键子不是方法的一部分,所以它不会被继承,说白了,就是如果父类的方法有synchronized,子类重写这个方法,synchronized不写也不会报错
synchronized 不能修饰接口
synchronized 不能修复构造方法,但是可以修饰构造方法里面的代码块
锁代码块就是我上面的那个例子写法了,这个就是锁的是某个对象中的某个代码,让它线程同步。
synchronized (this) {
System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");
count++;
}
我们看到这个synchronized (this) 里面的this,这里是要传入一个对象的,如果不用this也可以,也可以在这个类里面new一个其他对象效果也是一样的,比如
private Object obj=new Object();
@Override
public void run() {
while (count<15)
{
synchronized (obj)
{
System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");
count++;
}
try {
Thread.sleep(200);
}
catch (Exception e)
{
}
}
}
这里就用了obj这个new的对象,效果和this完全一样
public class MainClass {
public static class MyRun implements Runnable
{
public static int count=0;
@Override
public void run() {
while (count<15)
{
synchronized (this)
{
System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");
count++;
}
try {
Thread.sleep(200);
}
catch (Exception e)
{
}
}
}
public synchronized void dodo()
{
}
}
public static void main(String args[])
{
MyRun myRun1=new MyRun();
MyRun myRun2=new MyRun();
Thread threadA=new Thread(myRun1,"A");
Thread threadB=new Thread(myRun2,"B");
threadA.start();
threadB.start();
}
}
这个代码里面,
MyRun myRun1=new MyRun();
MyRun myRun2=new MyRun();
我new两个MyRun,我前面说过synchronized(this)只能锁某个对象,就是说threadA执行myRun1 threadB执行myRun2,互不干扰,synchronized只能锁自己的run1 或者run2 不能两个对象同时锁到,所以执行的结果是无序的。
ThreadName:A count:0
ThreadName:B count:0
ThreadName:B count:2
ThreadName:A count:2
ThreadName:B count:4
ThreadName:A count:4
ThreadName:A count:6
ThreadName:B count:6
ThreadName:A count:8
ThreadName:B count:8
如果我们想让两个线程有序执行,这个Count++操作,而且对run1,和run2都同时锁,应该怎么办呢???
答案是锁类,锁类的意思是不管是这个类new了多少对象,这个对象的所有方法,都会上锁,我们更改下代码看看结果,怎么锁类的:
synchronized (MyRun.class) {
System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");
count++;
}
我们看到只改动了 synchronized (MyRun.class)这里,其他代码都不变们,这个就把这个MyRun锁了,我们看看结果:
ThreadName:A count:0
ThreadName:B count:1
ThreadName:B count:2
ThreadName:A count:3
ThreadName:B count:4
ThreadName:A count:5
ThreadName:A count:6
ThreadName:B count:7
ThreadName:A count:8
结果是有序的,验证了我们的结果
public synchronized static void ddo()
这种方式其实和第三种效果是一样的,都是对类起作用,因为我们知道static修饰的方法是类方法,所以这个synchronized 也是作用于整个类
我们把上面的代码改下看看效果是不是一样:
public class MainClass {
public static class MyRun implements Runnable
{
public static int count=0;
@Override
public void run() {
ddo();
}
public synchronized static void ddo()
{
while (count<15)
{
System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");
count++;
try {
Thread.sleep(200);
}
catch (Exception e)
{
}
}
}
}
public static void main(String args[])
{
MyRun myRun1=new MyRun();
MyRun myRun2=new MyRun();
Thread threadA=new Thread(myRun1,"A");
Thread threadB=new Thread(myRun2,"B");
threadA.start();
threadB.start();
}
}
结果:
ThreadName:A count:0
ThreadName:A count:1
ThreadName:A count:2
ThreadName:A count:3
ThreadName:A count:4
ThreadName:A count:5
ThreadName:A count:6
ThreadName:A count:7
ThreadName:A count:8
因为A线程 先执行最后满足条件 while (count<15),所以B没有机会执行了,验证符合我们的预期
1、锁如果加在方法上面,或者在方法中的代码块形式,就是锁的这个对象,如果锁是静态方法中,或者代码块synchronized(A.class) 形式 就是锁的这个类,里面的所有方法都会同步。
2、谁拥有了锁,上面线程就拥有了控制这段代码的能力,其他的线程只能看着,只有释放了锁,其他线程才可以操作。
3、synchronized 消耗系统性能,所以能不加锁的逻辑,尽量不要加。
4、操作读写文件,或者数据库,有的时候多线程会出现不可预知的问题,所以要加入锁。