synchronized关键字在同步代码块中的应用(上)

synchronized修饰方法的弊端

如果A线程调用同步方法执行一个长时间的任务,那么B线程等待的时间就比较长,这种情况可以使用synchronized同步语句块来解决,以提高运行效率。

synchronized方法是将当前对象作为锁,而synchronized代码块是将任意对象作为锁。可以将锁看成一个标识,哪个线程持有这个标识,就可以执行同步方法

用同步代码块解决同步方法的弊端,提升效率

public class Task {
    private String getData1;
    private String getData2;

    public void doLongTimeTask() {
        try {
            System.out.println("begin task");
            Thread.sleep(3000);
            String privateGetData1 =
                    "长时间处理任务后从远程返回的值1 threadName=" + Thread.currentThread().getName();
            String privateGetData2 =
                    "长时间处理任务后从远程返回的值2 threadName=" + Thread.currentThread().getName();

            synchronized (this) {
                getData1 = privateGetData1;
                getData2 = privateGetData2;
                System.out.println(getData1);
                System.out.println(getData2);
                System.out.println("end task");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class ThreadA extends Thread {
    private Task task;

    public ThreadA(Task task) {
        super();
        this.task = task;
    }

    @Override
    public void run() {
        super.run();
        task.doLongTimeTask();
    }
}
public class ThreadB extends Thread {
    private Task task;

    public ThreadB(Task task) {
        super();
        this.task = task;
    }

    @Override
    public void run() {
        super.run();
        task.doLongTimeTask();
    }
}
public class Test {
    public static void main(String[] args) {
       Task task = new Task();
       ThreadA threadA = new ThreadA(task);
       ThreadB threadB = new ThreadB(task);

       threadA.start();
       threadB.start();
    }
}
output:
begin task
begin task
长时间处理任务后从远程返回的值1 threadName=Thread-1
长时间处理任务后从远程返回的值2 threadName=Thread-1
end task
长时间处理任务后从远程返回的值1 threadName=Thread-0
长时间处理任务后从远程返回的值2 threadName=Thread-0
end task

当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块。

synchronized代码块间的同步性

当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这说明synchronized使用的对象监视器是同一个,即使用的锁是同一个

public class MyObject {
    synchronized public void methodA() {
        try {
            System.out.println("begin methodA threadName=" + Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("methodA end");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    synchronized public void methodB() {
        try {
            System.out.println("begin methodB threadName=" + Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("methodB end");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class Test {
    public static void main(String[] args) {
       Service service = new Service();
       ThreadA threadA = new ThreadA(service);
       ThreadB threadB = new ThreadB(service);

       threadA.start();
       threadB.start();
    }
}
output:
A begin time=1652974614229
A end end=1652974616244
B begin time=1652974616244
B end end=1652974616244

println()方法也是同步的

jdk源码如下:

public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

    public void println(Object x) {
        String s = String.valueOf(x);
        synchronized (this) {
            print(s);
            newLine();
        }
    }

使用sychronized(this)代码块,执行按照顺序执行,输出的数据是完整的,不会出现信息交叉混乱的情况。

synchronized(this)代码块是锁定当前对象的

和synchronized方法一样,synchronized(this)代码块也是锁定当前对象的。

将任意对象作为锁

多个线程调用同一个对象中的不同名称的synchronized同步方法或synchronized(this)同步代码块时,调用的效果是按顺序执行,即同步。

synchronized同步方法的作用:

  1. 对其他synchronized同步方法或者synchronized(this)同步代码块调用呈同步效果。
  2. 同一时间只有一个线程可以执行synchronized同步方法中的代码。

synchronized(this)同步代码块的作用:

  1. 对其他synchronized同步方法或者synchronized(this)同步代码块调用呈同步效果。
  2. 同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码。

java还支持将“任意对象”作为锁来实现同步功能,这个“任意对象”大多数是实例变量及方法的参数,使用格式为synchronized(非this对象)。

锁非this对象的优点:

synchronized(非this)代码块中的程序与同步方法是异步的,因为有两把锁,不与其他锁this同步方法抢this锁,可以大大提高效率。

多个锁就是异步执行

public class Service {
    private String username;
    private String password;

    public void setUsername(String username, String password) {
        try {
            String anyString = new String();
            synchronized (anyString) {
                System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入同步块");
                this.username = username;
                Thread.sleep(3000);
                this.password = password;
                System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开同步块");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
public class Test {
    public static void main(String[] args) {
       Service service = new Service();
       ThreadA threadA = new ThreadA(service);
       ThreadB threadB = new ThreadB(service);

       threadA.start();
       threadB.start();
    }
}
output:
线程名称为:Thread-0在1653052403930进入同步块
线程名称为:Thread-1在1653052403945进入同步块
线程名称为:Thread-1在1653052406960离开同步块
线程名称为:Thread-0在1653052406960离开同步块

anyString 在两个线程中分别对应两个String对象,不是同一个锁,运行结果异步调用。

public class Service {
    private String username;
    private String password;

    private String anyString = new String();

    public void a() {
        try {
            synchronized (anyString) {
                System.out.println("a begin");
                Thread.sleep(3000);
                System.out.println("a end");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    synchronized public void b() {
        System.out.println("b begin");
        System.out.println("b end");
    }
}
output:
a begin
b begin
b end
a end

验证方法被调用是随机的

同步代码块并不能保证调用方法的线程的执行同步(顺序性),也就是线程调用方法的顺序是无序的,虽然在同步块中执行的顺序是同步的。

public class Service {
    private List list = new ArrayList();

    synchronized public void add(String username) {
        System.out.println("ThreadName=" + Thread.currentThread().getName() + "执行了add方法!");
        list.add(username);
        System.out.println("ThreadName=" + Thread.currentThread().getName() + "退出了add方法!");
    }
}
public class ThreadA extends Thread {
    private Service service;

    public ThreadA(Service service) {
        this.service = service;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            service.add("threadA" + (i + 1));
        }
    }
}
public class ThreadB extends Thread {
    private Service service;

    public ThreadB(Service service) {
        this.service = service;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            service.add("threadB" + (i + 1));
        }
    }
}
output:
ThreadName=A执行了add方法!
ThreadName=A退出了add方法!
ThreadName=B执行了add方法!
ThreadName=B退出了add方法!
ThreadName=B执行了add方法!
ThreadName=B退出了add方法!
ThreadName=B执行了add方法!
ThreadName=B退出了add方法!
ThreadName=A执行了add方法!
ThreadName=A退出了add方法!

不同步导致的逻辑错误及其解决方法

public class MyOneList {
    private List list = new ArrayList();

    synchronized public void add(String data) {
        list.add(data);
    }

    synchronized public int getSize() {
        return list.size();
    }
}

public class Service {
    public MyOneList addServiceMethod(MyOneList list, String data) {
        try {
            if (list.getSize() < 1) {
                Thread.sleep(2000);
                list.add(data);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return list;
    }
}

public class ThreadA extends Thread {
    private MyOneList list;
    public ThreadA(MyOneList list){
        this.list = list;
    }
    @Override
    public void run(){
        Service service = new Service();
        service.addServiceMethod(list,"A");
    }
}

public class ThreadB extends Thread {
    private MyOneList list;

    public ThreadB(MyOneList list) {
        this.list = list;
    }

    @Override
    public void run() {
        Service service = new Service();
        service.addServiceMethod(list, "B");
    }
}

output:
listSize=2

出现错误的原因是两个线程异步的方式返回list参数的size()大小,解决办法就是“同步化”

public class Service {
    public MyOneList addServiceMethod(MyOneList list, String data) {
        try {
            synchronized (list) {
                if (list.getSize() < 1) {
                    Thread.sleep(2000);
                    list.add(data);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return list;
    }
}
output:
listSize=1

细化验证3个结论

synchronized(非this对象x)格式的写法是将x对象本身作为“对象监视器”,可以得出3个结论:

  1. 当多个线程同时执行synchronized(x){}同步代码块时呈同步效果。
  2. 当其他线程执行x对象中synchronized同步方法时呈同步效果。
  3. 当其他线程执行x对象方法里面的synchronized(this)代码块时呈现同步效果。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值