java多线程 synchronized 同步,同步方法,同步代码块,同步实例

synchronized 同步,同步方法,同步代码块,同步实例

一、synchronized有啥用呢?

       一句话,解决多线程中的并发问题。

       具体在什么情况下使用到呢?

     (1)资源共享

     (2)多个线程同时操作同一个资源

       当多个线程中出现以上两种情况的时候,被操作的共同资源则会混乱,出现非预期结果, 

二、并发编程的原则
      1. 原子性

       原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

      2. 可见性

       简单来说,就是某些情况下,线程对于一些资源变量的修改并不会立马刷新到内存中,而是暂时存放在缓存,寄存器中。

这导致的最直接的问题就是,对共享变量的修改,另一个线程看不到。

        Java提供了volatile关键字来保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。另外,通过synchronized和Lock也能够保证可见性,synchronizedLock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。

     3. 有序性

        在Java内存模型中,允许编译器和处理器对指令进行重新排序,但是重新排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

 

我们在看看JVM关于synchronized的两条规定:

1)线程解锁前(退出synchronized代码块之前),必须把共享变量的最新值刷新到主内存中,也就是说线程退出synchronized代码块值后,主内存中保存的共享变量的值已经是最新的了
2)线程加锁时(进入synchronized代码块之后),将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意:加锁与解锁需要是同一把锁)

两者结合:线程解锁前对共享变量的修改在下次加锁时对其他线程可见


根据以上推出线程执行互斥代码的过程:

 1>获得互斥锁(进入synchronized代码块)
 2>清空工作内存
 3>从主内存拷贝变量的最新副本到工作内存
 4>执行代码
 5>将更改后的共享变量的值刷新到主内存
 6>释放互斥锁(退出synchronized代码块)

 

二、synchronized的三种应用方式

       Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:

       1、实例方法

       锁的是当前实例对象 ,进入同步代码前要获得当前实例的锁,此作用域内的synchronized锁 ,可以防止多个线程同时访 问这个对象synchronized方法synchronized(this) 和 synchronized 方法一样,锁定的都是当前对象,所以如果已调用其中一个synchronized方法,则该对象中其他synchronized(this) 或者 synchronized 方法都会被阻塞,因为它们使用的是同一个对象锁

      注意:    

1)一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法,但是可以访问其他非同步的方法

2)不同对象实例的synchronized方法是不相干预的。也就是说,其它线程可以同时访问此类下的另一个对象实例中的synchronized方法;

使用举例: 

public class MethodSync {
    
    /*
     * 测试 synchronized 修饰方法时锁定的是调用该方法的对象
     */
    public synchronized void method(String name){
        System.out.println(name + " Start a sync method");
        try{
            Thread.sleep(300);
        }catch(InterruptedException e){

        }
        System.out.println(name + " End the sync method");
    }
}


       2、静态同步方法

        锁的是当前类的class对象 ,进入同步代码前要获得当前类对象的锁

        此作用域下,可以防止多个线程同时访问这个类中的synchronized方法。也就是说此种修饰,可以对此类的所有对象实例起作用。

      使用举例:

public synchronized static void doLongTimeTaskA() {}


       3、同步代码块

          锁的是括号里面的对象,给指定对象加锁,进入同步代码库前要获得给定对象的锁。这个地方是跟第一种 实例方法锁  是一样的,锁的都是对象,但是代码块更灵活

         使用实例:

public class Task {
	public void doLongTimeTask() throws InterruptedException {
		for (int i = 0; i < 100; i++) {
			System.out.println("nosynchronized threadName=" + Thread.currentThread().getName() + " i=" + (i + 1));
			Thread.sleep(100);
		}
		System.out.println("-----------------------");
 
		synchronized (this) {
			for (int i = 0; i < 100; i++) {
				System.out.println("synchronized threadName=" + Thread.currentThread().getName() + " i=" + (i + 1));
				Thread.sleep(100);
			}
		}
	}
}

 

注意:

        这里synchronized (this) 包起来的代码块是同步的,这个方法里的其他内容是异步的。所以如果同时有多个线程执行

doLongTimeTask() ,则会看到同步代码块里的内容会被阻塞,这个方法里的其他内容则会异步执行

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值