栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

对象及变量的并发访问(二)——synchronized同步语句块(2)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

对象及变量的并发访问(二)——synchronized同步语句块(2)

目录
  • 7.将任意对象作为对象监视器
  • 8.细化验证3个结论
  • 9.静态同步synchronized方法和synchronized(class)代码块
  • 10.数据类型String的常量池特性

接上一篇文章——【对象及变量的并发访问(二)——synchronized同步语句块(1)】
网址为:https://blog.csdn.net/qq_44307209/article/details/125359613?spm=1001.2014.3001.5501

7.将任意对象作为对象监视器

  多个线程调用同一个对象中的不同名称的synchronized同步方法或synchronized(this)同步代码块时,调用的效果就是按顺序执行,也就是同步的,阻塞的。
  这说明synchronized同步方法或synchronized(this)同步代码块分别有两种作用。
  (1)synchronized同步方法
1)对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态
2)同一时间只有一个线程可以执行synchronized同步方法中的代码
  (2)synchronized(this)同步代码块
1)对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态
2)同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码
  在前面的学习中,使用synchronized(this)格式来同步代码块,其实Java还支持对“任意对象”作为“对象监视器”来实现同步的功能。这个“任意对象”大多数是实例变量及方法的参数,使用格式为synchronized(非this对象)。
  根据前面对synchronized(this)同步代码块的作用总结可知,synchronized(非this对象)格式的作用只有1种:synchronized(非this对象x)同步代码块。
1)在多个线程持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非 this对象x)同步代码块中的代码。
2)当持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非 this对象x)同步代码块中的代码。
  锁非this对象的优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但会受到阻塞,所以 影响效率;但如果使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁非this同步方法争抢this锁,则可以大大提高运行效率。
  使用synchronized(非this对象x)同步代码块进行同步时,对象监视器必须是同一个对象。如果不是同一个对象监视器,运行的结果就是异步调用了,会交叉运行。
  使用synchronized(非this对象x)同步代码块也可以解决脏读问题。

  创建项目synBlockString,类文件Service.java代码如下:

public class Service
{
    private String userNameParam;
    private String passwordParam;
    private String anyString = new String();
    public void setUserNamePassword(String userName, String password)
    {
        try
        {
            synchronized (anyString)
            {
                System.out.println("线程名称为:" + Thread.currentThread().getName() + 
                        "在 " + System.currentTimeMillis() + " 进入同步代码块");
                userNameParam = userName;
                Thread.sleep(3000);
                passwordParam = password;
                System.out.println("线程名称为:" + Thread.currentThread().getName() + 
                        "在 " + System.currentTimeMillis() + " 离开同步代码块");
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

自定义线程ThreadA,ThreadB类,代码如下:

import service.Service;
public class ThreadA extends Thread
{
    private Service service; 
    public ThreadA(Service service)
    {
        super();    
        this.service = service;
    }
    
    public void run()
    {
        service.setUserNamePassword("a", "aa");
    }
}
import service.Service;
public class ThreadB extends Thread
{
    private Service service; 
    public ThreadB(Service service)
    {
        super();    
        this.service = service;
    }
    
    public void run()
    {
        service.setUserNamePassword("b", "bb");
    }
}

运行类Run.java代码如下:

import service.Service;
import extthread.ThreadA;
import extthread.ThreadB;
public calss Run {
    public static void main(String[] args)
       {
           Service service = new Service();
           ThreadA a = new ThreadA(service);
           a.setName("A");
           a.start();
           ThreadB b = new ThreadB(service);
           b.setName("B");
           b.start();
       }
}

运行结果如下所示:

线程名称为:A在 1403758101404 进入同步代码块
线程名称为:A在 1403758104407 离开同步代码块
线程名称为:B在 1403758104407 进入同步代码块
线程名称为:B在 1403758107407 离开同步代码块

8.细化验证3个结论

  “synchronized(非this对象x)”格式的写法是将x对象本身作为“对象监视器”,这样就可以得出以下3个结论:
(1)当多个线程同时执行synchronized(x){}同步代码块时呈同步效果。
(2)当其他线程执行x对象中synchronized同步方法时呈同步效果。
(3)当其他线程执行x对象方法里面的synchronized(this)代码块时呈现同步效果。
但需要注意,如果其他线程调用不加 synchronized关键字的方法,则还是异步调用。

9.静态同步synchronized方法和synchronized(class)代码块

  关键字synchronized还可以用在static静态方法上,如果这样写是对当前的*.java文件对应的Class类进行持锁。
  synchronized关键字加到static静态方法上是给Class类上锁,而 synchronized关键字加到非static静态方法上是给对象上锁。
为了验证不是同一个锁,创建项目synLock,文件Service.java代码如下:

public class Service {

    synchronized public static void printA(){
        try {
            System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"进入静态同步方法printA()");
            Thread.sleep(3000);
            System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"离开静态同步方法printA()");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    synchronized public void printB(){
        try {
            System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"进入同步方法printB()");
            Thread.sleep(3000);
            System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"离开同步方法printB()");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printC(){
        synchronized (Service.class){
            try {
                System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"进入Class同步块printB()");
                Thread.sleep(3000);
                System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"离开Class同步块printB()");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

创建线程类ThreadA,代码如下:

public class ThreadA extends Thread {

    private Service service;

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

    @Override
    public void run() {
        super.run();
        service.printA();
    }
}

创建线程类ThreadB,代码如下:

public class ThreadB extends Thread {

    private Service service;

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

    @Override
    public void run() {
        super.run();
        service.printB();
    }
}

创建线程类ThreadC,代码如下:

public class ThreadC extends Thread {

    private Service service;

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

    @Override
    public void run() {
        super.run();
        service.printC();
    }
}

运行类Run.java的代码如下:

public class Run {

    public static void main(String[] args) {
        Service service = new Service();
        Service service1 = new Service();

        ThreadA threadA = new ThreadA(service);
        threadA.setName("a");
        ThreadB threadB = new ThreadB(service);
        threadB.setName("b");
        ThreadC threadC = new ThreadC(service1);
        threadC.setName("c");

        threadA.start();
        threadB.start();
        threadC.start();

    }
}

运行结果如下所示:

线程名称为:a在1655956757968进入静态同步方法printA()
线程名称为:b在1655956757968进入同步方法printB()
线程名称为:b在1655956760974离开同步方法printB()
线程名称为:a在1655956760974离开静态同步方法printA()
线程名称为:c在1655956760974进入Class同步块printB()
线程名称为:c在1655956763985离开Class同步块printB()

  异步的原因是持有不同的锁,一个是对象锁,另外一个是Class锁,而Class锁可以对类的所有对象实例起作用。

  同步synchronized(Class)代码块的作用和sunchronized static方法的作用一样,都是对当前的*.java文件对应的Class类进行持锁,都是Class锁。

  Class锁和对象锁不一样,对象锁是锁定当前对象,Class锁是对类的所有对象实例起作用。

10.数据类型String的常量池特性

  在JVM中具有String常量池缓存的功能。
  将synchronized(String)同步块与String 联合使用时,要注意String的常量池特性带来的影响。
创建项目名称StringAndSyn,类文件Service.java代码如下:

public class Service {
    public static void print(String stringParam) {
        try {
            synchronized (stringParam) {
                while (true) {
                    System.out.println(Thread.currentThread().getName());
                    Thread.sleep(1000);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

创建线程ThreadA.java代码如下:

public class ThreadA extends Thread {
    private Service service;

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

    @Override
    public void run() {
        service.print("AA");
    }
}

创建线程ThreadB.java代码如下:

public class ThreadB extends Thread {
    private Service service;

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

    @Override
    public void run() {
        service.print("AA");
    }
}

创建运行类Run.java代码如下:

public class Run {

    public static void main(String[] args) {

        Service service = new Service();

        ThreadA a = new ThreadA(service);
        a.setName("A");
        a.start();

        ThreadB b = new ThreadB(service);
        b.setName("B");
        b.start();

    }
}

运行结果如下所示:

A
A
A
A
A
A
A
A
A
A
A
A

  出现这样的情况就是因为String的两个值都是AA,两个线程持有相同的锁,所以造成线程B不能执行。这就是String常量池所带来的问题。因此在大多数的情况下,同步synchronized代码块都不使用String作为锁对象,而改用其他,比如new Object()实例化一个Obiect 对象,但它并不放入缓存中。


以上代码下载请点击该链接:https://github.com/Yarrow052/Java-package.git

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/988701.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号