当前位置: 首页 > wzjs >正文

两学一做晋中市网站域名备案查询 网站备案查询

两学一做晋中市网站,域名备案查询 网站备案查询,做网站服务器什么配置,网站做视频一个月有多少钱收入说到多线程编程,一定少不了线程安全这个话题。我们前面了解了线程的原理以及线程与进程的关系。线程之间共享资源,这就代表了在多线程编程中一定会产生冲突,所以我们需要在敲代码时保证线程安全,避免这样的问题发生。 我们先看一…

说到多线程编程,一定少不了线程安全这个话题。我们前面了解了线程的原理以及线程与进程的关系。线程之间共享资源,这就代表了在多线程编程中一定会产生冲突,所以我们需要在敲代码时保证线程安全,避免这样的问题发生。

我们先看一个代码案例

public class Test10 {static int count = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {count++;}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {count++;}});t1.start();t2.start();//保证线程都执行完Thread.sleep(1000);System.out.println(count);}
}

在我们看来,运行的结果应该是100000,但是事实并非如此。

运行结果:

1.随机调度

count++;

这段代码是看似是一个指令,但实际上是分步执行的。

分为三步的:

1.读取数据

2.修改数据

3.放回内存

在执行过程中,CPU资源是随时会被调度走的,也就是说,如果执行到了读取内存,有可能会被立刻调度走的。这就是所谓的随机调度。

为了解释上面的案例可以画一个时间线:

这也就是造成上面现象的原因之一。

那我们应该如何解决呢?

解决方案1:

利用join方法,在t1执行完后再执行t2,这样虽然能解决问题,但是失去了并发执行的意义,串行执行的效率也比较低。

public class Test10 {static int count = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {count++;}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {count++;}});t1.start();t1.join();t2.start();t2.join();//保证线程都执行完Thread.sleep(1000);System.out.println(count);}
}

2.锁(synchronized)

对于解决上述的线程问题,引入了锁这一概念。

锁是什么,我们可以举个具体的例子来了解一下:

我们可以把锁看成家里的锁,当家里没人时,门是从外面锁上的,这时拥有钥匙的人就可以打开锁并进去执行任务,但为了在执行任务的时候是安全的,所以会从里面锁上,这时外面的人就算有钥匙也打不开门,当里面的人执行完任务的出来后还会把门带上,这时外面拥有钥匙的人就可以开门进去了。这其实就是锁是如何解决线程安全问题的类似原理。

public class Test10 {static int count = 0;public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {synchronized (locker) {count++;}}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {synchronized (locker) {count++;}}});t1.start();t2.start();//        t1.start();
//        t1.join();
//        t2.start();
//        t2.join();//保证线程都执行完Thread.sleep(1000);System.out.println(count);}
}

运行结果:

我们先了解一下使用锁的格式:

synchronized(锁对象){}

这里的锁对象可以是任意引用类型的对象,但要保证在解决同一个非原子问题时是同一个对象。

synchronized的使用方法除了上述格式以外,还能用来修饰方法:

这里的锁对象是this。

class Counter{int count = 0;public synchronized void addCount(){count++;}
}public class Test11 {public static void main(String[] args) throws InterruptedException {Object locker = new Object();Counter counter = new Counter();Thread t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {counter.addCount();}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {counter.addCount();}});t1.start();t2.start();Thread.sleep(100);System.out.println(counter.count);}
}

运行结果:

其实这样的解决方法也就是将局部的任务给串行化,只不过比直接将整个线程串行化来的含蓄,性能降低的少。

可重入

在Java中synchronized具有可重入的特点。

synchronized (locker) {synchronized (locker) {count++;}}

我们知道,锁是具有互斥性的,也就是在上锁后是需要解锁后才能让下一个拥有锁对象的任务执行,那上面这段代码就会形成一个死锁的现象,当进入第一个锁后,会遇到第二个锁,但是想要进入第二个synchronized是需要从第一个synchronized中出来的,但是要想从第一个synchronized中出来就需要进入第二个synchronized,所以这就形成了一个死循环,可以叫死锁。

Java的开发人员为了解决这一问题就赋予了synchronized可重入这一属性,也就是在上述情况下不会出现死锁的现象,Java会自动识别出来。

如果有很多层synchronized嵌套的话,当第一次进入的synchronized结束时,这把锁才会解开。

死锁 

上面讲可重入性的时候讲到了死锁这一概念,这里就详细讲讲死锁。

造成死锁有三种情况:

1.一个线程同一个锁加多次,这也是讲述可重入性时举的例子。

2.N个线程,M把锁。

public class Test12 {public static void main(String[] args) {Object locker1 = new Object();Object locker2 = new Object();Thread t1 = new Thread(()->{synchronized (locker1){try {System.out.println("t1获取了locker1!");//确保t2先拿到locker2Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker2){System.out.println("t1获取了两把锁!");}}});Thread t2 = new Thread(()->{synchronized (locker2){try {System.out.println("t2获取了locker2!");//确保线程t1先拿到locker1Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker1){System.out.println("t2获取了两把锁!");}}});t1.start();t2.start();}
}

运行结果:

进程并没有结束,使用jconsole查看线程状态,发现是BLOCKED,也就锁造成的无上限的阻塞等待。这是因为在线程t1拿到locker1和t2拿到locker2的情况下,t1要想拿到locker2就必须要让t2解锁,而t2要想解锁,就需要拿到locker1,但是locker1在t1手中,所以就形成了死循环,也就构成了死锁。

3.哲学家就餐问题

假设有七个哲学家围着一个桌子吃饭,每两个人中间放一根筷子,这样的话当一个人用筷子吃饭的时候,他两侧的人都没法用餐。

在一种极端情况下,每个哲学家都拿起他左手边的筷子,这样所有哲学家都在等待对方放下筷子,就形成了死锁。

public class Test13 {public static void main(String[] args) {Object locker1 = new Object();Object locker2 = new Object();Object locker3 = new Object();Object locker4 = new Object();Object locker5 = new Object();Thread t1 = new Thread(()->{synchronized (locker1){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker5){}}});Thread t2 = new Thread(()->{synchronized (locker2){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker1){}}});Thread t3 = new Thread(()->{synchronized (locker3){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker2){}}});Thread t4 = new Thread(()->{synchronized (locker4){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker3){}}});Thread t5 = new Thread(()->{synchronized (locker5){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker4){}}});t1.start();t2.start();t3.start();t4.start();t5.start();}
}

各线程状态: 

解决方案:

1.对筷子按顺序进行编号,先拿到左右小的编号的筷子,拿到后再拿左右大的编号的筷子。

刚开始1号先吃到饭,吃完后放下1号和7号筷子,2号拿到1号筷子,7号拿到7号筷子,7号可以就餐,以此类推,所有人都可以完成就餐。

public class Test13 {public static void main(String[] args) {Object locker1 = new Object();Object locker2 = new Object();Object locker3 = new Object();Object locker4 = new Object();Object locker5 = new Object();Thread t1 = new Thread(()->{synchronized (locker1){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker5){}}});Thread t2 = new Thread(()->{//保证t1先拿到locker1,如果t2先拿到locker1,还是会形成死锁try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker1){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker2){}}});Thread t3 = new Thread(()->{synchronized (locker2){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker3){}}});Thread t4 = new Thread(()->{synchronized (locker3){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker4){}}});Thread t5 = new Thread(()->{synchronized (locker4){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker5){}}});t1.start();t2.start();t3.start();t4.start();t5.start();}
}

运行结果:

 4.死锁总结:

造成死锁的原因:

1.锁具有互斥性(锁的基本特性)

当一个锁被一个线程获取之后,当别的线程想要获取这个锁的时候,会线程阻塞。

2.锁不可抢占(锁的基本特性)

当这个锁已经被获取时,别的线程是不能强行抢占这个锁的, 必须等待获取。

3.请求和保持

当一个线程已经有至少一个锁的时候,尝试获取别的锁遇到阻塞,这时候该线程也不会放弃原来的锁。

4.循环等待

线程1等待线程2,线程2等待线程3,线程3等待线程4,线程4等待线程5,线程5等待线程1,这样就产生了死循环。

解决方案:

1.把嵌套的锁改为并列的锁。(基于N个线程,M把锁的代码例子)

public class Test14 {public static void main(String[] args) {Object locker1 = new Object();Object locker2 = new Object();Thread t1 = new Thread(()->{synchronized (locker1){System.out.println("t1拿到locker1!");
//                try {
//                    Thread.sleep(100);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }}synchronized (locker2){System.out.println("t1拿到locker2!");}});Thread t2 = new Thread(()->{synchronized (locker2){System.out.println("t2拿到locker2!");
//                try {
//                    Thread.sleep(100);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }}synchronized (locker1){System.out.println("t2拿到locker1!");}});t1.start();t2.start();}
}

运行结果:

2.规定加锁顺序编号递增/递减(基于哲学家就餐问题)

代码案例在上述哲学家就餐问题中的Test13.

3.java标准库中的线程安全类:

StringBuffer,Hashtable,Vector,ConcurrentHashMap,String。。。。。。

前三个不推荐使用。

不安全类:
StringBuilder,ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet。

以StringBuffer为例,查看标准库中的代码,发现其内部是由简单加synchronized实现的,当面对比较复杂的情况时,很有可能会出现bug~~

4.wait/notify

在有些情况下我们需要让某个线程处于阻塞状态,在完成某些任务后再进行唤醒。

注意在调用wait/notify时必须实在synchronized的代码块中,并且必须是相同的锁对象才行。

wait下的线程状态:WAITING

public class Test16 {public static void main(String[] args) {Object locker = new Object();Thread t1 = new Thread(()->{synchronized (locker) {System.out.println("线程t1wait......");try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("线程t1被唤醒!");}});Thread t2 = new Thread(()->{synchronized (locker) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t2线程尝试唤醒t1......");locker.notify();}});t1.start();t2.start();}
}

运行结果:

在线程wait期间,该线程是主动放弃了CPU资源的,是解锁状态,暂时不会参与锁竞争。

这种情况下,当使用notify唤醒时只能唤醒其中一个,并且是随机的,这就有很大的不确定性在里面,所以java标准库中还提供了notifyAll方法,能够唤醒所有相同锁对象的wait。

对于notify是随即唤醒这一点还有可能会造成线程饿死,所谓线程饿死也就是某个线程长时间没有吃到CPU的资源。

public class Test17 {public static void main(String[] args) {Object l1 = new Object();Thread t1 = new Thread(()->{synchronized (l1){try {l1.wait();System.out.println("t1被唤醒!");} catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread t2 = new Thread(()->{synchronized (l1){try {l1.wait();System.out.println("t2被唤醒!");}    catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread t3 = new Thread(()->{synchronized (l1){try {l1.wait();System.out.println("t3被唤醒!");} catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread t4 = new Thread(()->{System.out.println("输入任意内容唤醒所有线程:");Scanner sc = new Scanner(System.in);sc.next();synchronized (l1){l1.notifyAll();}});t1.start();t2.start();t3.start();t4.start();}
}

运行结果:

 

http://www.dtcms.com/wzjs/551261.html

相关文章:

  • 打代码做网站的软件wordpress主题怎么添加视频播放器
  • 查询网 网站查询高邮建设局网站
  • 做网站 需求分享10个国外优秀的平面设计网站
  • 微信的网站怎么做学做烘培的网站
  • 网站运营方案模板光明随心订网站怎么做
  • 电商网站建设费用预算网站开发定制
  • 四川专业网站建设费用seo优化技术培训
  • 品牌设计网站大全网站建设的市场调研
  • 上海个人网站制作公司新的网站做淘宝客
  • 淘宝客网站免费做适合美工的网站
  • 百度联盟怎么做网站夫妻网络网站建设
  • h5网站建设价格站长工具seo综合查询收费吗
  • 郑州网站建设创业网站开发年薪
  • 黄图网站有哪些 推荐seo基础入门免费教程
  • 上海中学门户网站登陆wordpress整站密码
  • 成都建设银行网站焦作市网站建设公司
  • 品牌网站建设报价表网站建设方案策划书
  • 北京工商注册代理记账wordpress安全优化教程
  • 查域名网站闸北网站推广公司
  • 苏州网站推广软件十堰网站建设_网站制作_软件开发_网店培训 优易
  • 企业营销型网站策划务wordpress 系统找不到指定的文件.
  • 欢迎访问中国建设银行网站大足网站建设
  • 无锡网站制作企业wordpress改不了语言
  • wordpress的静态页面保存在哪里山东seo网络营销推广
  • 百度网站推广服务商做电子商务网站需要什么软件
  • 头条站长平台深圳招聘网站排名
  • 双语网站费用wordpress 默认登录地址
  • 网页设计模板的网站国内企业邮箱
  • 网站名字词wordpress 主题和插件
  • 高端网站建设系统php网站开发心得体会