java线程sys_Java多线程之synchronized(二)

为了解决“非线程安全”带来的问题,上一节中使用的办法是用关键字synchronized修饰多个线程可能同时访问到的方法,但是这样写是存在一定的弊端的,比如线程A调用一个用synchronized修饰的同步方法,这个方法要执行很长时间,那么其它的线程必须无条件的等线程A执行完释放掉对象锁,当然前提是其他的线程也要访问这个同步方法。这种情况就可以用synchronized代码块来解决。在解决之前我先附上一段没优化之前的方法,这样就可以直观的看到效果差异。

证明synchronized方法的弊端,代码如下:

public class Entity {

public static long beginTime1;

public static long endTime1;

public static long beginTime2;

public static long endTime2;

}

public class Task_Synchronized {

private String getData1;

private String getData2;

//两个子线程要调用的公共方法

public synchronized void doLongTimeTask() {

try {

System.out.println("begin task");

Thread.sleep(3000);

//getData1和getdata2实际过程中可以是两个非常耗时的操作,这样看起来效果更名

getData1 = "长时间处理任务后从远程返回的值1 threadName="

+ Thread.currentThread().getName();

getData2 = "长时间处理任务后从远程返回的值2 threadName="

+ Thread.currentThread().getName();

System.out.println(getData1);

System.out.println(getData2);

System.out.println("end task");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

public static void main(String[] args) throws InterruptedException {

Task_Synchronized task = new Task_Synchronized();

MyThread1 t1 = new MyThread1(task);

t1.start();

MyThread2 t2 = new MyThread2(task);

t2.start();

Thread.sleep(10000);

// 因为线程1和线程2哪个先执行不一定,所以比较了一下时间,开始的时间取比较小的值,结束的时间取较大的值

long beginTime = Entity.beginTime1;

if (Entity.beginTime2 < Entity.beginTime1) {

beginTime = Entity.beginTime2;

}

long endTime = Entity.endTime1;

if (Entity.endTime2 > Entity.endTime1) {

endTime = Entity.endTime2;

}

System.out.println("耗时" + (endTime - beginTime) / 1000 + "s");

}

//第一个线程

public static class MyThread1 extends Thread {

private Task_Synchronized task;

public MyThread1(Task_Synchronized task) {

super();

this.task = task;

}

@Override

public void run() {

super.run();

Entity.beginTime1 = System.currentTimeMillis();

task.doLongTimeTask();

Entity.endTime1 = System.currentTimeMillis();

}

}

//第二个线程

public static class MyThread2 extends Thread {

private Task_Synchronized task;

public MyThread2(Task_Synchronized task) {

super();

this.task = task;

}

@Override

public void run() {

// TODO Auto-generated method stub

super.run();

Entity.beginTime2 = System.currentTimeMillis();

task.doLongTimeTask();

Entity.endTime2 = System.currentTimeMillis();

}

}

运行结果如下:从Task_Synchronized 类可以看出,synchronized修饰的是doLongTimeTask()方法,从执行结果也可以看出syschronized修饰方法的执行顺序是这样的:Thread—0必须把同步的方法全部执行完,释放掉对象锁之后,Thread—1才可以执行,也就是说无论哪个线程先抢上CPU,就要执行到底之后,另一个线程才可以上CPU执行,这样执行下来用时间是6s。

149b8f77d5d05c947ad8b69f184c725e.png

那么接下来看看用synchronized代码块怎么解决这个弊端,我写了一个例子,如下:

public class Task_Synchronized {

private String getData1;

private String getData2;

//没有synchronized修饰

public void doLongTimeTask() {

try {

System.out.println("begin task");

Thread.sleep(3000);

String privateGetData1 = "长时间处理的任务1 threadName="

+ Thread.currentThread().getName();

String privateGetData2 = "长时间处理的任务1 threadName="

+ Thread.currentThread().getName();

//synchronized代码块

synchronized (this) {

getData1 = privateGetData1;

getData2 = privateGetData2;

}

System.out.println(getData1);

System.out.println(getData2);

System.out.println("end task");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

执行结果如下:只需要修改一下doLongTimeTask()这个方法即可,用synchronized代码块来代替synchronized修饰方法,从运行结果可以看到时间只用3s,时间缩短了是由于线程Thread—0访问Task_Synchronized类的同步代码块时,线程Thread—1仍然可以访问Task_Synchronized类里的非同步方法。在这里为什么会想到只同步变量getData1和getData2呢,我以前说过出现“非线程安全”的原因,其中有一个原因就是有多个线程同时访问成员变量时可能会出现“脏读”现象。

cd0134e96bbafc7047b30b8bb4ebd39a.png

但是还有两个问题需要验证,那就是syschronized代码块里的内容真的是同步执行的吗?这个this真的代表当前类的对象锁吗?下面我写了一个例子来验证一下,如下:

public static void main(String[] args) {

Task task = new Task();

ThreadA a = new ThreadA(task);

a.setName("A");

ThreadB b = new ThreadB(task);

b.setName("B");

a.start();

b.start();

}

public static class ThreadA extends Thread {

private Task task;

public ThreadA(Task task) {

super();

this.task = task;

}

@Override

public void run() {

super.run();

task.doLongTimeTask();

}

}

public static class ThreadB extends Thread {

private Task task;

public ThreadB(Task task) {

super();

this.task = task;

}

@Override

public void run() {

super.run();

task.doLongTimeTask();

}

}

}

class Task {

public voiddoLongTimeTask() {

for (int i = 0; i < 100; i++) {

System.out.println("noSynchronized threadName="

+ Thread.currentThread().getName() + " i=" + (i + 1));

}

System.out.println("*********************************");

synchronized (this) {

for (int i = 0; i < 100; i++) {

System.out.println("Synchronized threadName="

+ Thread.currentThread().getName() + " i=" + (i + 1));

}

}

}

运行结果如下:由图(a)可以看出来,两个线程在执行第一for循环的时候,是不同步交叉执行的,由图b1、b2、c1、c2可以看出来线程A排队执行完,线程B才开始执行,所以synchronized代码块是同步的。

e55866d606e87cd987c8c6f058aca5fb.png   

bea64717f372e8b3778a510e962f399d.png    

2aed2e2699fb9a00002b7e25c05fb154.png

图(a)图(b1)图(b2)

900a63d5bf8983923e524809d17e50a7.png   

1fdce05582aab04b5b988e2fd823feac.png

图(c1)               图(c2)

那么怎么验证synchronized使用的“对象监视器”是一个呢,也就是this代表的是当前类的对象锁。需要证明只有一个线程释放掉当前对象锁,其它的线程才可以执行。我写了一个例子,如下:

public static void main(String[] args) {

MyService service = new MyService();

ThreadA a = new ThreadA(service);

a.setName("A");

ThreadB b = new ThreadB(service);

b.setName("B");

a.start();

b.start();

}

public static class ThreadA extends Thread {

private MyService service;

public ThreadA(MyService service) {

super();

this.service = service;

}

@Override

public void run() {

super.run();

service.serviceMethodA();

}

}

public static classThreadB extends Thread {

private MyService service;

public ThreadB(MyService service) {

super();

this.service = service;

}

@Override

public void run() {

super.run();

service.serviceMethodB();

}

}

}

class MyService {

public void serviceMethodA() {

synchronized (this) {

try {

System.out.println("A begin time=" + System.currentTimeMillis());

Thread.sleep(5000);

System.out.println("A end time=" + System.currentTimeMillis());

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

public void serviceMethodB() {

synchronized (this) {

try {

System.out.println("B begin time=" + System.currentTimeMillis());

Thread.sleep(2000);

System.out.println("B end time=" + System.currentTimeMillis());

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

运行结果如下:从代码上可以看出,两个synchronized代码块里都有休眠方法,但是并没有影响线程的执行顺序,如果两个this不是同一把对象锁,那么在休眠的这段时间,线程肯定会出现交替执行的,从结果也可以看出来,线程A执行完之后,线程B才开始执行的,说明当线程A访问MyService的同步代码块serviceMethodA的时候,线程B对同步代码块serviceMethodB的访问将被阻塞。所以“对象监视器”是同一个。

df0e9449d099c70167638b49ec5f6d8e.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的影城管理系统,源码+数据库+论文答辩+毕业论文+视频演示 随着现在网络的快速发展,网上管理系统也逐渐快速发展起来,网上管理模式很快融入到了许多生活之中,随之就产生了“小徐影城管理系统”,这样就让小徐影城管理系统更加方便简单。 对于本小徐影城管理系统的设计来说,系统开发主要是采用java语言技术,在整个系统的设计中应用MySQL数据库来完成数据存储,具体根据小徐影城管理系统的现状来进行开发的,具体根据现实的需求来实现小徐影城管理系统网络化的管理,各类信息有序地进行存储,进入小徐影城管理系统页面之后,方可开始操作主控界面,主要功能包括管理员:首页、个人中心、用户管理、电影类型管理、放映厅管理、电影信息管理、购票统计管理、系统管理、订单管理,用户前台;首页、电影信息、电影资讯、个人中心、后台管理、在线客服等功能。 本论文主要讲述了小徐影城管理系统开发背景,该系统它主要是对需求分析和功能需求做了介绍,并且对系统做了详细的测试和总结。具体从业务流程、数据库设计和系统结构等多方面的问题。望能利用先进的计算机技术和网络技术来改变目前的小徐影城管理系统状况,提高管理效率。 关键词:小徐影城管理系统;Spring Boot框架,MySQL数据库
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值