Java多线程详记(三)——Synchronized关键字的用法

在Java多线程编程下,当线程运行环境中有临界资源时,需要保证该资源在同一时刻只能被一个线程使用访问,此时就需要Synchronized关键字

synchronized的使用方式

synchronized在java中有三种应用方式
1.修饰实例方法,对当前实例加锁,进入同步代码前要获得当前实例的锁
2.修饰静态方法,对当前类对象加锁,进入同步代码前要获得当前类对象的锁
3.修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁

对静态、实例方法加锁

对静态方法进行加锁,锁住的是当前类的Class对象,执行静态方法的前提是得到得到类锁

public class ThreadSynDemo1 {
	public static void main(String[] args) {
		new Thread_A().start();
		new Thread_B().start();	
	}
}

class StaSynMethod{
	 static synchronized void compute1() {
		while(true) {
			System.out.println(Thread.currentThread().getName()+"正在使用(静态方法111)");
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}	
	}
	 static synchronized void compute2() {
			while(true) {
				System.out.println(Thread.currentThread().getName()+"正在使用(静态方法222)");
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}	
		}
	 
	void out() {
		while(true) {
			System.out.println(Thread.currentThread().getName()+"正在使用(实例方法)");
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}	
}
class Thread_A extends Thread{
	@Override
	public void run() {
		super.run();
		StaSynMethod.compute1();
	}
}
class Thread_B extends Thread{
	@Override
	public void run() {
		super.run();
		//情况1
		StaSynMethod s = new StaSynMethod();
		s.out();
		//情况2
		//StaSynMethod.compute1();
		//情况3
		//StaSynMethod.compute2();
	}
}

情况1:
在这里插入图片描述
结论:给类的静态方法加锁(锁住的是类对象),并不影响其他线程使用该类的实例方法(因为访问静态 synchronized 方法占用的锁是当前类的class对象,而访问非静态 synchronized 方法占用的锁是当前实例对象锁


情况2:
在这里插入图片描述
结论:给类的静态方法加锁(锁住的是类对象),其他线程不能正常使用执行该静态方法(没有获得类锁),毕竟一个对象只有一把锁,当一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁


情况3:
在这里插入图片描述
结论:给类的静态方法加锁(锁住的是类对象)后,该类的其他被锁定的静态方法不能其他线程执行。


再补充来个情况4: 将compute2()静态方法的synchronized去掉执行

//去点原有的synchronized
static void compute2() {
			while(true) {
				System.out.println(Thread.currentThread().getName()+"正在使用(静态方法222)");
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}	
		}

在这里插入图片描述
结论:给类的静态方法加锁(锁住的是类对象)后,其他未加锁的静态方法任然被其他线程执行使用。


再再补充情况5: 也是比较常见的一种情况,验证调用同一对象时,多线程能否同时执行该对象的被锁住同一实例方法,或者被锁住不同的实例方法
代码修改如下

    synchronized void out() {
		while(true) {
			System.out.println(Thread.currentThread().getName()+"正在使用(实例方法)");
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	//新增实例方法out2()
	 synchronized void out2() {
		while(true) {
			System.out.println(Thread.currentThread().getName()+"正在使用(实例方法222)");
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

将同一对象传参进多个线程中

class Thread_A extends Thread{
	private StaSynMethod s;
	public Thread_A(StaSynMethod s) {
		this.s = s;
	}

	@Override
	public void run() {
		super.run();
		s.out();
	}
}
class Thread_B extends Thread{
	private StaSynMethod s;
	public Thread_B(StaSynMethod s) {
		this.s = s;
	}
	@Override
	public void run() {
		super.run();
		//情况5		
		s.out();
	}
}

在这里插入图片描述
结论:给同一对象(注意:必须是同一对象)的实例方法加锁(锁住的是该实例对象)后,该实例方法不能被其他线程执行使用。


情况6:执行被锁住不同的实例方法
在这里插入图片描述
结论:给同一对象(注意:必须是同一对象)的实例方法加锁(锁住的是该实例对象)后,其他被加锁的实例方法不能被其他线程执行使用。


为什么要给静态方法加锁

上面栗子仅为测试,并没有说出使用静态方法锁的场景究竟是什么。其实,当多线程操作共享数据数据时,线程安全是没有保障的,就是说可能会出现脏读,幻读等现象,所以为静态方法加锁,可保证同一时间只能有一个线程执行该静态方法(参考情况2),从而保证同一时间数据的读写是唯一的,安全的。

对代码块加锁

这是比较推荐的加锁方式,试想:当我们的方法体庞大,耗时长的情况下,我们仍然给方法提加锁,可能效率上是非常底下的,这在实际的生产中是不允许的,所以我们可以选择给需要同步的一小部分代码加锁就可以了,这样提高了效率,安全性上也同样有保障

public class ThreadSynDemo2 implements Runnable {
    static int i=0;
    @Override
    public void run() {
        //省略其他耗时操作....
        synchronized(this){
            for(int j=0;j<1000000;j++){
                    i++;
              }
            System.out.println(i);
        }
    }
    public static void main(String[] args) throws InterruptedException {
    	ThreadSynDemo2 t = new ThreadSynDemo2();
        Thread t1=new Thread(t);
        Thread t2=new Thread(t);
        t1.start();
        t2.start();
    }
}

在这里插入图片描述
可以看到数据是安全的,在多个线程同时访问下,没有出现同时加的情况

代码块的测试结论如下:
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

初步测试为此,如有错误,欢迎留言
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值