重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
这篇文章主要讲解了“什么是synchronized”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“什么是synchronized”吧!
成都创新互联公司服务项目包括黄梅网站建设、黄梅网站制作、黄梅网页制作以及黄梅网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,黄梅网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到黄梅省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!
上面也说了,synchronized可以写在方法上,也可以写在代码块上,只是有可能锁的类型不同,如果写在方法上,锁的就是当前类的对象,在代码块上使用,可以指定锁的对象。如果作用在静态代码块上,锁的就是指定的类的class对象了
synchronized锁属于全自动类型的,自动上锁解锁,而且在执行代码的过程中,如果抛异常了,synchronized也会自动释放锁
synchronized锁是可重入锁,就是当前线程调用A的方法,A方法中调用B方法,可以直接调用,不用竞争锁。jvm这样设计,也是为了防止死锁的产生,所以,在这里你应该也能明白了,为什么当前线程访问A方法时,其他线程访问其他方法时,不能让其访问的原因了。或者这样说,T1线程访问A方法时,T2线程也来访问A方法,肯定不被允许的,这不不用多说,因为锁的是一个对象,那个对象被T1线程锁着呢,T2线程当然获取不了锁。那相同的,T2线程去访问B方法时,B方法中锁的不也是同一个对象吗,也就是说T2线程还要去获取T1线程在A方法中锁定的对象的锁。
threadlocal线程私有变量,举个例子你就知道了:
public class ThreadLocalTest { class Person { String name = "zhangsan"; } @Test public void threadLocal1() { ThreadLocaltl = new ThreadLocal (); Person person = new Person(); tl.set(person); new Thread(new Runnable() { @Override public void run() { System.out.println("新启线程"+tl.get()); } }).start(); System.out.println(tl.get()); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
新启的线程打印person为null,也就是说,通过threadlocal设置的对象,只能在当前线程中使用,其他线程获取为空。
volatile关键字,请点击跳转
重点说下notify和wait,因为这里面的坑,稍不注意就掉进去了,之前写过一篇,我再举个生产者、消费者的例子: 需求:有2个生产者线程、10个消费者线程,如果容器满了,则生产者暂停,如果容器空了,则消费者暂停
public class ProducerAndConsumer { //有2个生产者线程、10个消费者线程,如果容器满了,则生产者暂停,如果容器空了,则消费者暂停 final private LinkedList list = new LinkedList(); final private int maxValue = 10; /** * 生产者,按照要求:当容器满了,就停止。 */ public synchronized void producer(Object o) { /** * 最初的实现是用if,但是为什么又改成if而改成while呢:(这是一个坑,很容易掉里面) * 1.你想啊,如果此时,容器满了,然后第一个线程到这后,判断if逻辑,然后执行wait方法进行等待,并【释放当前对象锁】 * 2.然后cpu调起哪个线程,是随机选择的,如果此时,又调起了一个生产者线程,那么也执行到了这个wait方法。 * 3.目前已经有两个线程在wait这了,然后cpu执行消费者线程消费了1个,现在容器中还有9个, * 然后消费者线程唤醒通过notifyall唤醒线程,唤醒了这两个生产者线程,那么: * 3.1 首先第一个生产者线程开始执行,从wait处开始往下执行,添加元素,容器又满了,释放锁 * 3.2 然后第二个生产者线程开始执行,也是从wait处开始执行,因为使用的不是while循环,所以 * 会直接往下执行,也就是添加元素,那么容器大小就不是10 个了。 * 4.所以,使用wait时的场景,99%都是与while循环配合使用 * 5.如果wait和notify不写在synchronized锁里面,会抛IllegalMonitorStateException异常,但是 * 为什么必须在synchronized锁中呢,那么肯定会出现wait调用发生在调用notify之后的情况。[参考博客](https://www.cnblogs.com/set-cookie/p/8686218.html) */ // if(list.size()==maxValue) { while(list.size()==maxValue) { try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } list.add(o); //容器中有东西了,唤醒等待的线程线程进行消费(唤醒的线程中包括生产者线程和消费者线程),其主要目的是消费者线程 this.notifyAll(); } /** * 消费者 */ public synchronized Object consumer() { while(list.size()==0) { try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //我拿走东西了,唤醒等待的线程进行生产(唤醒的线程中包括生产者线程和消费者线程),其主要目的是生产者线程 Object o = list.removeFirst(); this.notifyAll(); return o; } public static void main(String[] args) { ProducerAndConsumer pcTest = new ProducerAndConsumer(); //启动消费者线程 for(int i=0;i<10;i++) { new Thread(()->{ for(int j=0;j<10;j++) { System.out.println(pcTest.consumer()+"aa"); } },"c"+i) .start(); } try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //启动生产者线程 for(int i=0;i<2;i++) { new Thread(()->{ for(int j=0;j<30;j++) { pcTest.producer(Thread.currentThread().getName()); } },"p"+i) .start(); } }
线程停止stop、interrupt方法, thread.stop(),是直接把你干掉了,没有一点余地的直接kill掉,都不会进入catch中,所以不到万不得已,不要用stop方法。 thread.interrupt()时,线程还是可以进下面catch中的代码的。 但是这两个方法都不被推荐,更理想的办法是定义变量,:
public class ThreadStopByArgs { /** * 通过变量的形式,是线程进行停止 * */ public static void main(String[] args) { ThreadRunnerTest threadRunnerTest = new ThreadRunnerTest(); Thread thread = new Thread(threadRunnerTest); thread.start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("stop"); threadRunnerTest.changeFlag(); } } class ThreadRunnerTest implements Runnable { private volatile boolean flag = true; public void run() { int i=0; while(flag) { //System.out.println(i++);测试多线程时,如果使用syso输出,要小心使用,因为syso中有sync } } public void changeFlag(){ flag = false; } }
其他的方法,比如sleep、yield、join、stop、interrupt等方法,请您自行学习吧,没啥好记得 锁必须锁的是同一个对象的基础上的,如同:你卫生间还能有多个门吗?也就是说,访问共享的资源时,只有一个门能让你去访问,这样才能控制共享资源和线程的安全性。安全的意义并不只针对车祸那种类型的,只要预期结果被改变是因为在执行过程中被其他因素干扰导致的,就属于不安全。
感谢各位的阅读,以上就是“什么是synchronized”的内容了,经过本文的学习后,相信大家对什么是synchronized这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!