- 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
多个线程调用同一个对象中的不同名称的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关键字的方法,则还是异步调用。
关键字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锁是对类的所有对象实例起作用。
在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



