java多线程4(synchronized方法、代码块)

在之前的文章中创建线程,启动多个线程去完成不同的任务,这样在多处理器的机器上效率会高一些,但是如果多线程都要访问同一个共享资源,这时如果不用一些方法让线程同步执行,则就会出现脏读等现象,执行后得到的结果也不是我们想要的结果,出现这种情况是因为操作是非线程安全安全而导致的,如果是线程安全的则不会出现这种情况,下面来看看什么是线程安全什么是非线程安全的定义:


线程安全:

当多个线程类并发操作某类的某个方法(在该方法内部)来修改这个类的某个成员变量的值,不会出现错误(即正常的结果)则我们就说类的这个方法是线程安全的。

某类的某方法是否线程安全的关键是:

  (1)、该方法是否修改该类的成员变量;

   (2)、是否给该方法加锁(是否使用synchronized关键字修饰),当然还有synchronized 代码块以及Lock等这些之后再说。

线程不安全:

当多个线程类并发操作某类的某个方法(在该方法内部)来修改这个类的某个成员变量的值,修改时很容易出现错误、脏读(不是正确的结果),我们就说这个方法是线程不安全的,如果要把这个方法变成是线程安全的,则用synchronized 关键字来修饰该方法即可,当然还有其他方法比如synchronized 代码块,Lock等,这些之后再说。


注意:这里强调一下,类的方法中定义的局部变量永远是线程安全的,因为这个变量的值是存贮在栈内存中即线程的私有内存中,不涉及到到多线程共享的问题。

    上面线程安全的定义中就说了,修改类的某个成员变量的值,因为类的成员变量是在jvm堆内存中而堆内存是多线程共享的所以会出现非线程安全。


线程安全可以使用synchronized方法 、synchronized(this) 代码块、synchronized static 、synchronized(class)、volatile等来实现,下面先来看看同步方法

一、synchronized 方法(同步方法)

1、如果多个线程访问同一个对象中的同步方法时,一定是线程安全的,因为线程执行同步方法时需要获得该对象的对象锁,而一个对象只有一个对象锁,同一时刻只有一个线程能得到对象锁,直到这个同步方法结束该线程才会释放对象锁,所以多个线程中再同一时刻只有一个线程在执行同步方法。

2、如果多个线程访问多个对象,则jvm会创建多个对象锁,而每个对象最多一个!

3、线程中调用synchronized声明的方法一定是排队运行的(因为对象锁之后一个,同一时刻只有一个线程能获得这个对象锁)

4、只有共享资源的读写访问才需要同步化,如果不是共享资源那么根本不需要同步,

5、synchronized方法的执行需要获得对象锁才能执行,所以当A线程调用了anyObject对象加入synchronized关键字的X方法时,A线程获得了anyObject对象锁,所以其他线程必须等A线程执行完毕才可以调用X方法,但B线程可随意调用其他的非synchronized同步方法。

6、当A线程调用anyObject对象加入synchronized关键字的X方法时,A线程就获取了X方法所在的对象锁,所以其他线程必须等A线程执行完毕才能调用X方法,而线程B如果调用声明了synchronized关键字的非X方法时,必须等A线程将X方法执行完,也就是释放对象锁后才可以调用。

7、synchronized具有锁重入功能,也就是说synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的,当然如果不具有这个功能就会出现死锁。

8、如果出现异常则线程所持有的对象锁将被释放。

9、同步不具有继承性,也就是父类中的synchronized方法在子类中不具有synchronized如果子类中需要则要自己添加synchronized来重写。

10、synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。

ok 这些就是在使用synchronized 方法时带来的同步性以及一些特性!

二、synchronized(this)(同步代码块)

上面总结的这些都是synchronized方法的相关知识点,但是synchronized方法有些时候使用起来不太方便,所以就需要使用synchronized(this) /synchronized(非this)代码块来实现,下面我们来看看synchronized 代码块。

1、synchronized 方法的弊端,比如A线程调用同步方法执行一个耗时比较长的任务,那么B线程调用另一个同步方式时只能等,而如果使用synchronized代码块对两个方法加锁的对象不同的话就能解决这问题。这里的同步代码块也就是synchronized(非this)这样多个对象生成多个锁,多个线程可以调用执行。

2、代码块间的同步:

在synchronized方法时我们知道如果A线程访问anyObject的synchronized X方法则其他线程必须等到A线程结束后才能访问X方法,而在A线程结束前其他线程是阻塞的而synchronized(this)代码块也是同样的,当一个线程访问anyObject的一个synchronized(this)同步代码块时其他线程对同一个anyObject中的所有其他synchronized(this)同步代码块的访问也将被阻塞。

3、synchronized同步方法和synchronized(this)同步代码块的作用

(1)、synchronized同步方法

1)、对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态。

2)、同一时刻只有一个线程可以执行synchronized同步方法中的代码。

(2)、synchronized(this)同步代码块

1)、对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态。

2)、同一时刻只有一个线程可以执行synchronized(this)代码块中的代码。

三、synchronized(非this)(同步代码块)

1、锁非this对象具有一定的优点,如果在一个类中有很多个synchronized方法这时虽然能实现同步,但会受到阻塞,所以影响运行效率,但如果使用同步代码块锁非this对象,则synchronized(非this) 代码块中的程序与同步方法/synchronized(this)就是异步了,不与其他锁this同步方法争抢this锁,则可大大提高运行效率。

2、synchronized(非this对象X)格式的写法就是将X对象本身作为“对象监视器”,这样就可以得到三个如下结论:

(1)、当多个线程同时执行synchronized(X)同步代码块时呈同步效果。

(2)、当其他线程执行X对象中synchronized同步方法时呈同步效果。

(3)、当其他线程执行X对象方法中的synchronized(this)代码时呈同步效果,如果其他线程调用不加synchronized关键字/synchronized(this)代码块时还是异步的。

四、synchronized staic同步方法和synchronized(class)代码块

1、关键字synchronized还可以应用在static静态方法上,如果这样写那就是对当前这个 *.java这个文件对应的Class类进行持锁。

2、synchronized关键字加到static 静态方法上是给class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁即一个是给类上锁一个是给对象上锁。

3、对象锁和类锁是不同的两个锁,所以 同一个类的 static synchronized方法和synchronized方法被类和对象调用执行的时候是异步而不是同步。

4、Class锁可以对类的所有对象实例起作用。

5、同步synchronized(class)代码块的作用其实和synchronized static方法的作用是一样的,其实synchronized(class)和synchronized static的关系与synchronized(this) 和synchronized方法的关系是一样的。

6、String  对象作为对象锁时要注意,String常量池的缓存有可能是同一个对象。

7、同一个线程可以获取多个对象的对象锁。

package test3;

public class ExtendThread extends Thread {
	Object ob1= new Object();
	Object ob2= new Object();
	@Override
	public void run(){
		synchronized(ob1) {
		System.out.println("ob1lock"+Thread.currentThread().getName());
		synchronized(ob2) {
			System.out.println(" obj2 lock");
		}
		}
	}
}

这段代码证明一个线程可以获取多个对象的对象锁!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值