马士兵java并发编程的代码,照抄过来,做个记录。

一、分析下面面试题

/**
* 曾经的面试题:(淘宝?)
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束
*
* 分析下面这个程序,能完成这个功能吗?
* @author mashibing
*/
package yxxy.c_019; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit; public class MyContainer1 { List lists = new ArrayList(); public void add(Object o) {
lists.add(o);
} public int size() {
return lists.size();
} public static void main(String[] args) {
MyContainer1 c = new MyContainer1(); new Thread(() -> {
for(int i=0; i<10; i++) {
c.add(new Object());
System.out.println("add " + i); try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start(); new Thread(() -> {
while(true) {
if(c.size() == 5) {
break;
}
}
System.out.println("t2 结束");
}, "t2").start();
}
}
分析:
不能完成这个功能;
添加volatile关键字,修改为如下:
 
二、添加volatile:
/**
* 曾经的面试题:(淘宝?)
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束
*
* 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,该怎么做呢?
* @author mashibing
*/
package yxxy.c_019; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit; public class MyContainer2 { //添加volatile,使t2能够得到通知
volatile List lists = new ArrayList(); public void add(Object o) {
lists.add(o);
} public int size() {
return lists.size();
} public static void main(String[] args) {
MyContainer2 c = new MyContainer2(); new Thread(() -> {
for(int i=0; i<10; i++) {
c.add(new Object());
System.out.println("add " + i); try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start(); new Thread(() -> {
while(true) {
if(c.size() == 5) {
break;
}
}
System.out.println("t2 结束");
}, "t2").start();
}
}

但是上面代码还存在两个问题:

1)由于没加同步,c.size()等于5的时候,假如另外一个线程又往上增加了1个,实际上这时候已经等于6了才break,所以不是很精确;

2)浪费CPU,t2线程的死循环很浪费cpu

三、使用wait和notify

/**
* 曾经的面试题:(淘宝?)
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束
*
* 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,该怎么做呢?
*
* 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁
* 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以
*
* 阅读下面的程序,并分析输出结果
* 可以读到输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出
* 想想这是为什么?
* @author mashibing
*/
package yxxy.c_019; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit; public class MyContainer3 { //添加volatile,使t2能够得到通知
volatile List lists = new ArrayList(); public void add(Object o) {
lists.add(o);
} public int size() {
return lists.size();
} public static void main(String[] args) {
MyContainer3 c = new MyContainer3(); final Object lock = new Object(); new Thread(() -> {
synchronized(lock) {
System.out.println("t2启动");
if(c.size() != 5) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 结束");
} }, "t2").start(); try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
e1.printStackTrace();
} new Thread(() -> {
System.out.println("t1启动");
synchronized(lock) {
for(int i=0; i<10; i++) {
c.add(new Object());
System.out.println("add " + i); if(c.size() == 5) {
lock.notify();
} try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "t1").start(); }
}

分析:

1)解释wait和notify方法:

使用wait和notify必须进行锁定,如果没有锁定这个对象,你就不能调这个对象的wait和notify方法;
wait和notify是调用被锁定对象的wait和notify方法。
比如有一个对象A,两个线程1,2来访问这个对象A,第一个线程1的访问过程中假如条件没有满足,想让这个线程1暂停,等着;这时候怎么办的呢?
首先线程1要锁定对象A,然后调用这个对象A的wait方法,线程1就进入等待状态,同时释放锁,别的线程可以进来;
 
什么时候线程1会再次启动呢?只有再调用这个对象A的notify方法。notify方法会启动一个正在这个对象A上等待的某一个线程,或者是调用notifyAll(),
notifyAll方法是叫醒在这个对象上正在等待的所有线程。
 
notify无法指定的通知启动哪一个具体的线程,没法指定,由cpu线程调度器自己帮你找一个线程运行。
 
 
 
2)为什么size=5了,t2线程没有结束?
由于notify不会释放锁,即便你通知了t2,让它起来了,它起来之后想往下运行,wait了之后想重新继续往下运行是需要重新得到lock这把锁的,可是很不幸的是t1已经把这个锁锁定了,所以只有等t1执行完了,t2才会继续执行。

四、

/**
* 曾经的面试题:(淘宝?)
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束
*
* 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,该怎么做呢?
*
* 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁
* 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以
*
* 阅读下面的程序,并分析输出结果
* 可以读到输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出
* 想想这是为什么?
*
* notify之后,t1必须释放锁,t2退出后,也必须notify,通知t1继续执行
* 整个通信过程比较繁琐
* @author mashibing
*/
package yxxy.c_019; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit; public class MyContainer4 { //添加volatile,使t2能够得到通知
volatile List lists = new ArrayList(); public void add(Object o) {
lists.add(o);
} public int size() {
return lists.size();
} public static void main(String[] args) {
MyContainer4 c = new MyContainer4(); final Object lock = new Object(); new Thread(() -> {
synchronized(lock) {
System.out.println("t2启动");
if(c.size() != 5) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 结束");
//通知t1继续执行
lock.notify();
} }, "t2").start(); try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
e1.printStackTrace();
} new Thread(() -> {
System.out.println("t1启动");
synchronized(lock) {
for(int i=0; i<10; i++) {
c.add(new Object());
System.out.println("add " + i); if(c.size() == 5) {
lock.notify();
//释放锁,让t2得以执行
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "t1").start(); }
}

流程图:

上面的通信过程太繁琐了,有没有简单点的办法?

五、

/**
* 曾经的面试题:(淘宝?)
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束
*
* 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,该怎么做呢?
*
* 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁
* 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以
*
* 阅读下面的程序,并分析输出结果
* 可以读到输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出
* 想想这是为什么?
*
* notify之后,t1必须释放锁,t2退出后,也必须notify,通知t1继续执行
* 整个通信过程比较繁琐
*
* 使用Latch(门闩)替代wait notify来进行通知
* 好处是通信方式简单,同时也可以指定等待时间
* 使用await和countdown方法替代wait和notify
* CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行
* 当不涉及同步,只是涉及线程通信的时候,用synchronized + wait/notify就显得太重了
* 这时应该考虑countdownlatch/cyclicbarrier/semaphore
* @author mashibing
*/
package yxxy.c_019; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; public class MyContainer5 { // 添加volatile,使t2能够得到通知
volatile List lists = new ArrayList(); public void add(Object o) {
lists.add(o);
} public int size() {
return lists.size();
} public static void main(String[] args) {
MyContainer5 c = new MyContainer5(); CountDownLatch latch = new CountDownLatch(1); new Thread(() -> {
System.out.println("t2启动");
if (c.size() != 5) {
try {
latch.await(); //也可以指定等待时间
//latch.await(5000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 结束"); }, "t2").start(); try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
e1.printStackTrace();
} new Thread(() -> {
System.out.println("t1启动");
for (int i = 0; i < 10; i++) {
c.add(new Object());
System.out.println("add " + i); if (c.size() == 5) {
// 打开门闩,让t2得以执行
latch.countDown();
} try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }, "t1").start(); }
}
 
CountDownLatch(1),CountDown往下数,当1变为0的时候门闩就开了,latch.countDown()调用一次数就往下-1;
latch.await(),门闩的等待是不需要锁定任何对象的;

java高并发编程(二)的更多相关文章

  1. [ 高并发]Java高并发编程系列第二篇--线程同步

    高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...

  2. java高并发编程(一)

    读马士兵java高并发编程,引用他的代码,做个记录. 一.分析下面程序输出: /** * 分析一下这个程序的输出 * @author mashibing */ package yxxy.c_005; ...

  3. 关于Java高并发编程你需要知道的“升段攻略”

    关于Java高并发编程你需要知道的"升段攻略" 基础 Thread对象调用start()方法包含的步骤 通过jvm告诉操作系统创建Thread 操作系统开辟内存并使用Windows ...

  4. Java高并发编程基础三大利器之CountDownLatch

    引言 上一篇文章我们介绍了AQS的信号量Semaphore<Java高并发编程基础三大利器之Semaphore>,接下来应该轮到CountDownLatch了. 什么是CountDownL ...

  5. java高并发编程(三)

    java高并发主要有三块知识点: synchronizer:同步器,在多个线程之间互相之间怎么进行通讯,同步等: 同步容器:jdk提供了同步性的容器,比如concurrentMap,concurren ...

  6. java高并发编程(五)线程池

    摘自马士兵java并发编程 一.认识Executor.ExecutorService.Callable.Executors /** * 认识Executor */ package yxxy.c_026 ...

  7. java高并发编程(四)高并发的一些容器

    摘抄自马士兵java并发视频课程: 一.需求背景: 有N张火车票,每张票都有一个编号,同时有10个窗口对外售票, 请写一个模拟程序. 分析下面的程序可能会产生哪些问题?重复销售?超量销售? /** * ...

  8. [高并发]Java高并发编程系列开山篇--线程实现

    Java是最早开始有并发的语言之一,再过去传统多任务的模式下,人们发现很难解决一些更为复杂的问题,这个时候我们就有了并发. 引用 多线程比多任务更加有挑战.多线程是在同一个程序内部并行执行,因此会对相 ...

  9. Java高并发编程(四)

    一.Executor执行器 1.Executor接口,java线程池框架中的顶层接口,提供一个execute方法来执行任务 import java.util.concurrent.Executor; ...

随机推荐

  1. Tomcat介绍、安装jdk、安装Tomcat、配置Tomcat监听80端口

    1.Tomcat介绍 2.安装jdk下载:wget -c http://download.oracle.com/otn-pub/java/jdk/10.0.1+10/fb4372174a714e6b8 ...

  2. Ubuntu端口命令---查看端口占用及关闭

    Ubuntu查看端口使用情况,使用netstat命令: 查看已经连接的服务端口(ESTABLISHED) netstat -a 查看所有的服务端口(LISTEN,ESTABLISHED) netsta ...

  3. Vs code 设置

    { "git.ignoreMissingGitWarning": true, "terminal.integrated.shell.windows": &quo ...

  4. (9)SQL的注入(致命的漏洞)

    用户登陆网站的时候进行账户验证输入特殊的格式和字符会触发一个漏洞,不需要密码直接登录成功 import pymysql username = input('请输入账号: ')password = in ...

  5. Python的函数基础

    引子 现在老板让你写一个监控程序,24小时全年无休的监控你们公司网站服务器的系统状况,当cpu\memory\disk等指标的使用量超过阀值时即发邮件报警,你掏空了所有的知识量,写出了以下代码 whi ...

  6. 安装排错 max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]

    https://blog.csdn.net/cookzrk/article/details/80179006 转载:https://my.oschina.net/u/2510243/blog/8105 ...

  7. css与html 与js的基础语法

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Sample_2-23456.a ...

  8. ZOJ2112 Dynamic Rankings

    题意 Dynamic Rankings Time Limit: 10 Seconds      Memory Limit: 32768 KB The Company Dynamic Rankings ...

  9. Apache按天截断日志工具,无法生成访问日志

    rotatelogs实现Apache的日志轮转 httpd.conf配置如下: ErrorLog "logs/error_log" CustomLog "|$/usr/l ...

  10. angular学习第一天——安装batarang踩到的那些坑儿

      angularjs作为一个新兴的JavaScript框架,因其具有不少新特性,比如mvc开发模块,双向数据绑定等等,使其名声大噪.我也久闻其大名,然而因为时间问题,一直都没有去接触过他.这几天工作 ...