1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > java synchronized静态同步方法与非静态同步方法 同步语句块

java synchronized静态同步方法与非静态同步方法 同步语句块

时间:2022-11-16 07:18:30

相关推荐

java synchronized静态同步方法与非静态同步方法 同步语句块

大纲:java线程知识体系

对代码进行同步控制我们可以选择同步方法,也可以选择同步块,这两种方式各有优缺点。同步块不仅可以更加精确的控制对象锁,还可以控制锁的作用域,何谓锁的作用域?锁的作用域就是从锁被获取到其被释放的时间。而且可以选择要获取哪个对象的对象锁。但是如果在使用同步块机制时,如果使用过多的锁也会容易引起死锁问题,同时获取和释放所也有代价,而同步方法,它们所拥有的锁就是该方法所属的类的对象锁,换句话说,也就是this对象,而且锁的作用域也是整个方法,这可能导致其锁的作用域可能太大,也有可能引起死锁,同时因为可能包含了不需要进行同步的代码块在内,也会降低程序的运行效率。而不管是同步方法还是同步块,我们都不应该在他们的代码块内包含无限循环,如果代码内部要是有了无限循环,那么这个同步方法或者同步块在获取锁以后因为代码会一直不停的循环着运行下去,也就没有机会释放它所获取的锁,而其它等待这把锁的线程就永远无法获取这把锁,这就造成了一种死锁现象。

详细解说一下同步方法的锁,同步方法分为静态同步方法与非静态同步方法,先看代码再说理论。以下是成功解决线程安全问题的案例,但如果show方法不是静态的即使被synchronized修饰也无法解决线程问题,大家可以先试一下

class Windows implements Runnable {private static int ticketNum = 10;public static synchronized void show(){String name = Thread.currentThread().getName();if(ticketNum > 0) {try {//这一步是为了演示错票,原理是当前线程进入了if语句陷入沉睡的时候票被卖光,//然后当该线程苏醒时再来一次ticketNum--产生0号这个非法票Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name +"卖出第" + ticketNum + "张票");ticketNum--;}}@Overridepublic void run() {while (true){show();}}}public class ThreadTest{public static void main(String[] args) {Windows windows = new Windows();Windows windows1 = new Windows();Thread thread1 = new Thread(windows);thread1.setName("窗口1");Thread thread2 = new Thread(windows);;thread2.setName("窗口2");Thread thread3 = new Thread(windows1);thread3.setName("窗口3");thread1.start();thread2.start();thread3.start();}}

运行结果,没有线程安全问题

为何show必须是静态同步才能解决线程问题?

1 所有的非静态同步方法用的都是同一把锁——实例对象本身(this,本例中有windows和windows1),Synchronized修饰非静态方法,实际上是对调用该方法的对象加锁,俗称“对象锁”。也就是说如果一个实例对象的非静态同步方法获取锁(windows对象的锁)后,该实例对象的其他非静态同步方法的执行或者同类的线程(由windows构造的线程)在此执行该同步方法时必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象(windows1)的非静态同步方法因为跟该实例对象(windows)的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁,也就是说windows的锁只对windows(通过windows对象构造的线程)有效而不会影响windows1),自然无法保证线程安全。

2 而所有的静态同步方法用的也是同一把锁——类对象本身(Windwos.class),Synchronized修饰静态方法,实际上是对该类对象加锁,俗称“类锁”。两个不同的对象(windows和windows1)。一旦一个静态同步方法获取锁(Windows.class)后,其他的静态同步方法的执行或者其它类型的线程(由windows1构造的线程)再次调用该静态同步方法时都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!

同理,看以下代码,我们选择使用Windows.class作为同步锁,可以保证线程安全。但是如果我们还是选用this做同步锁依然无法保证线程安全,大家可以试一下

class Windows implements Runnable {private static int ticketNum = 10;private Object obj = new Object();//obj也可以用作同步锁@Overridepublic void run() {String name = Thread.currentThread().getName();while (true){//这里也推荐用this(this代表的是main中的windows对象)或Windows.classsynchronized (Windows.class){if(ticketNum > 0) {try {//这一步是为了演示错票,原理是当前线程进入了if语句陷入沉睡的时候票被卖光,//然后当该线程苏醒时再来一次ticketNum--产生0号这个非法票Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + "卖出第" + ticketNum + "张票");ticketNum--;}}}}}public class ThreadTest{public static void main(String[] args) {Windows windows = new Windows();Windows windows1 = new Windows();Thread thread1 = new Thread(windows);thread1.setName("窗口1");Thread thread2 = new Thread(windows);;thread2.setName("窗口2");Thread thread3 = new Thread(windows1);thread3.setName("窗口3");thread1.start();thread2.start();thread3.start();}}

对于同步块,由于其锁是可以选择的,所以只有使用同一把锁的同步块之间才有着竞争关系,这就得具体情况具体分析了,同步块的锁是可以选择的,但是不是可以任意选择的!

①对一个null对象加锁会产生异常

②对不同的对象加锁也违背了同步的初衷!一个经常发生的错误就是选用了错误的锁对象,因此必须注意:同步是基于实际对象而不是对象引用的!不要选择一个可能会在锁的作用域中改变值的实例变量作为锁对象!本例中的obj用作锁对象的话对于整个类的声明周期来说都不会发生改变,synchronized (obj){}等价于synchronized (this){}

③无论是synchronized (obj){}还是synchronized (this){}都只能保证同一个Runnable实现类的对象构造的形参保证线程安全,如果要不同类型(windows1和windows2)的线程保证线程安全,需要使用synchronized (Windwos.class){}来锁住整个类

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。