当前位置: 首页 > 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/474260.html

相关文章:

  • html5+css3网站模板网站编辑seo
  • 网站被挂木马怎么办关键词难易度分析
  • 临沂市住房和城乡建设委员会网站经典软文案例
  • sublime做网站词爱站的关键词
  • 西安做网站好的公司谷歌推广怎么做最有效
  • 美国 做网站广告公司业务推广
  • 高密哪里有做网站的适合seo软件
  • 自助建站平台免费最新的全国疫情
  • 网站建设 app开发 小程序杭州seo托管公司推荐
  • aspcms 网站地图百度服务中心人工24小时电话
  • 江门网站制作建设怎么注册个人网站
  • 菏泽 做网站 多少钱专业代写软文
  • 怎么做分享软件的网站google推广怎么做
  • 免费做任务赚钱的网站有哪些百度seo怎么查排名
  • 吉林省党风廉政建设官方网站衡水seo营销
  • 北京麒麟网站建设今日百度小说排行榜风云榜
  • 网站建设 唐山怎么找关键词
  • 网站做软件有哪些内容免费注册推广网站
  • 大型网站开发 优帮云网络营销和网站推广的区别
  • 用手机如何制作网页链接seo排名技术教程
  • 个人网站建设工作室360提交网站收录入口
  • 移动网站制作自己的网站怎么建立
  • 有高并发,高访问量网站开发软文是什么样子的
  • 福州市城乡建设局网站东莞seo关键词
  • 佛山网站建设 奇锐科技百度电脑版下载官网
  • 永州做网站百度小说排行榜2021
  • 动态网站开发的感想网络广告电话
  • 外贸网站模板外贸网站建设seo优化工作怎么样
  • 住房城乡建设厅网站营口seo
  • 网页制作公司简介文登seo排名