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

网站制作与app开发哪个要难一点做网站的团队业绩怎么写

网站制作与app开发哪个要难一点,做网站的团队业绩怎么写,潍坊网站建设熊掌号,0基础学网站开发目录 线程的概述多线程的创建方式一#xff1a;继承Thread类方式二#xff1a;实现Runnable接口方式三#xff1a;利用Callable接口、FutureTask类来实现。Thread常用的方法 线程安全问题线程安全问题概述线程安全问题案例取钱案例描述模拟代码如下#xff1a;执行结果 线程… 目录 线程的概述多线程的创建方式一继承Thread类方式二实现Runnable接口方式三利用Callable接口、FutureTask类来实现。Thread常用的方法 线程安全问题线程安全问题概述线程安全问题案例取钱案例描述模拟代码如下执行结果 线程同步概述线程同步的常见方案1. 同步代码块2. 同步方法3. Lock锁 线程通信概述线程通信案例案例代码实现 线程池线程池概述线程池创建线程池执行Runnable任务代码案例 线程池执行Callable任务代码案例核心线程数量到底应该配置多少呢 线程池工具类Executors并发、并行和生命周期并发和并行1. 什么是进程、线程2. 什么是并发3. 什么是并行4. 多线程到底是并发还是并行呢 线程的生命周期 乐观锁与悲观锁 线程的概述 什么是线程 线程(Thread)是一个程序内部的一条执行流程。 程序中如果只有一条执行流程那这个程序就是单线程的程序 什么是多线程 多线程是指从软硬件上实现的多条执行流程的技术多条线程由CPU负责调度执行) 如何在程序中创建出多条线程 Java是通过java.lang.Thread 类的对象来代表线程的。 多线程的创建 方式一继承Thread类 定义一个子类MyThread继承线程类java.lang.Thread重写run()方法创建MyThread类的对象调用线程对象的start方法启动线程启动后还是执行run方法的 示例代码如下 主线程类 public class ThreadTest1 {// main方法是由一条默认的主线程负责执行。public static void main(String[] args) {// 3、创建MyThread线程类的对象代表一个线程Thread t new MyThread();// 4、启动线程自动执行run方法的t.start(); // main线程 t线程for (int i 1; i 5; i) {System.out.println(主线程main输出 i);}} }子线程类 /*** 1、让子类继承Thread线程类。*/ public class MyThread extends Thread{// 2、必须重写Thread类的run方法Overridepublic void run() {// 描述线程的执行任务。for (int i 1; i 5; i) {System.out.println(子线程MyThread输出 i);}} }方式一优缺点 优点编码简单 缺点线程类已经继承Thread无法继承其他类不利于功能的扩展。 多线程的注意事项 1、启动线程必须是调用start方法不是调用run方法。 直接调用run方法会当成普通方法执行此时相当于还是单线程执行。 只有调用start方法才是启动一个新的线程执行。 2、不要把主线程任务放在启动子线程之前。 这样主线程一直是先跑完的相当于是一个单线程的效果了。方式二实现Runnable接口 定义一个线程任务类MyRunnable实现Runnable接口重写run()方法创建MyRunnable任务对象把MyRunnable任务对象交给Thread处理。调用线程对象的start()方法启动线程 示例代码如下 定义一个任务类 /*** 1、定义一个任务类实现Runnable接口*/ public class MyRunnable implements Runnable{// 2、重写runnable的run方法Overridepublic void run() {// 线程要执行的任务。for (int i 1; i 5; i) {System.out.println(子线程输出 》 i);}} }主线程类 /*** 多线程的创建方式二实现Runnable接口。*/ public class ThreadTest2 {public static void main(String[] args) {// 3、创建任务对象。Runnable target new MyRunnable();// 4、把任务对象交给一个线程对象处理。// public Thread(Runnable target)new Thread(target).start();for (int i 1; i 5; i) {System.out.println(主线程main输出 》 i);}} }方式二的优缺点 优点任务类只是实现接口可以继续继承其他类、实现其他接口扩展性强。 缺点需要多一个Runnable对象。 线程创建方式二的匿名内部类写法 可以创建Runnable的匿名内部类对象。再交给Thread线程对象。再调用线程对象的start()启动线程。 代码示例 /*** 多线程创建方式二的匿名内部类写法。*/ public class ThreadTest2_2 {public static void main(String[] args) {// 1、直接创建Runnable接口的匿名内部类形式任务对象Runnable target new Runnable() {Overridepublic void run() {for (int i 1; i 5; i) {System.out.println(子线程1输出 i);}}};new Thread(target).start();// 简化形式1new Thread(new Runnable() {Overridepublic void run() {for (int i 1; i 5; i) {System.out.println(子线程2输出 i);}}}).start();// 简化形式2new Thread(() - {for (int i 1; i 5; i) {System.out.println(子线程3输出 i);}}).start();for (int i 1; i 5; i) {System.out.println(主线程main输出 i);}} }方式三利用Callable接口、FutureTask类来实现。 前两种线程创建方式都存在的一个问题 假如线程执行完毕后有一些数据需要返回他们重写的run方法均不能直接返回结果。 怎么解决这个问题 JDK 5.0提供了Callable接口和FutureTask类来实现多线程的第三种创建方式。 这种方式最大的优点可以返回线程执行完毕后的结果 方式三 实现步骤 创建任务对象 定义一个类实现Callable接口重写call方法封装要做的事情和要返回的数据。 把Callable类型的对象封装成FutureTask线程任务对象。把线程任务对象交给Thread对象。调用Thread对象的start方法启动线程。线程执行完毕后、通过FutureTask对象的的get方法去获取线程任务执行的结果。 示例代码如下 创建任务对象类 import java.util.concurrent.Callable;/*** 1、让这个类实现Callable接口*/ public class MyCallable implements CallableString {private int n;public MyCallable(int n) {this.n n;}// 2、重写call方法Overridepublic String call() throws Exception {// 描述线程的任务返回线程执行返回后的结果。// 需求求1-n的和返回。int sum 0;for (int i 1; i n; i) {sum i;}return 线程求出了1- n 的和是 sum;} }主线程类 import java.util.concurrent.Callable; import java.util.concurrent.FutureTask;/*** 线程的创建方式三实现Callable接口。*/ public class ThreadTest3 {public static void main(String[] args) throws Exception {// 3、创建一个Callable的对象CallableString call new MyCallable(100);// 4、把Callable的对象封装成一个FutureTask对象任务对象// 未来任务对象的作用// 1、是一个任务对象实现了Runnable对象.// 2、可以在线程执行完毕之后用未来任务对象调用get方法获取线程执行完毕后的结果。FutureTaskString f1 new FutureTask(call);// 5、把任务对象交给一个Thread对象new Thread(f1).start();CallableString call2 new MyCallable(200);FutureTaskString f2 new FutureTask(call2);new Thread(f2).start();// 6、获取线程执行完毕后返回的结果。// 注意如果执行到这儿假如上面的线程还没有执行完毕// 这里的代码会暂停等待上面线程执行完毕后才会获取结果。String rs f1.get();System.out.println(rs);String rs2 f2.get();System.out.println(rs2);} }FutureTask的API 方式三的优缺点 优点线程任务类只是实现接口可以继续继承类和实现接口扩展性强可以在线程执行完毕后去获取线程执行的结果 缺点编码复杂一点。线程创建方式三的优缺点 优点线程任务类只是实现接口可以继续继承类和实现接口扩展性强可以在线程执行完毕后去获取线程执行的结果。 缺点编码复杂一点。 Thread常用的方法 Thread提供了很多与线程操作相关的方法 示例代码 子线程 public class MyThread extends Thread{public MyThread(String name){super(name); // 为当前线程设置名字了}Overridepublic void run() {// 哪个线程执行它它就会得到哪个线程对象。Thread t Thread.currentThread();for (int i 1; i 3; i) {System.out.println(t.getName() 输出 i);}} }主方法 /*** Thread的常用方法。*/ public class ThreadTest1 {public static void main(String[] args) {Thread t1 new MyThread(1号线程);// t1.setName(1号线程);t1.start();System.out.println(t1.getName()); // Thread-0Thread t2 new MyThread(2号线程);// t2.setName(2号线程);t2.start();System.out.println(t2.getName()); // Thread-1// 主线程对象的名字// 哪个线程执行它它就会得到哪个线程对象。Thread m Thread.currentThread();m.setName(最牛的线程);System.out.println(m.getName()); // mainfor (int i 1; i 5; i) {System.out.println(m.getName() 线程输出 i);}} }掌握sleep方法,join方法的作用 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;/*** sleep方法,join方法的作用。*/ public class ThreadTest2 {public static void main(String[] args) throws Exception {System.out.println(Runtime.getRuntime().availableProcessors());for (int i 1; i 5; i) {System.out.println(i);// 休眠5sif(i 3){// 会让当前执行的线程暂停5秒再继续执行// 项目经理让我加上这行代码如果用户交钱了我就注释掉Thread.sleep(5000);}}// join方法作用让当前调用这个方法的线程先执行完。Thread t1 new MyThread(1号线程);t1.start();t1.join();Thread t2 new MyThread(2号线程);t2.start();t2.join();Thread t3 new MyThread(3号线程);t3.start();t3.join();} }线程安全问题 线程安全问题概述 什么是线程安全问题 多个线程同时操作同一个共享资源的时候可能会出现业务安全问题。 线程安全问题出现的原因 存在多个线程在同时执行同时访问一个共享资源存在修改该共享资源 线程安全问题案例 取钱案例描述 需求 小明和小红是一对夫妻他们有一个共同的账户余额是10万元模拟2人同时去取钱10万。 分析 1. 需要提供一个账户类接着创建一个账户对象代表2个人的共享账户。 2. 需要定义一个线程类用于创建两个线程分别代表小明和小红 3. 创建2个线程传入同一个账户对象给2个线程处理。 4. 启动2个线程同时去同一个账户对象中取钱10万。出现线程安全问题的步骤 模拟代码如下 先定义一个共享的账户类 public class Account {private String cardId; // 卡号private double money; // 余额。public Account() {}public Account(String cardId, double money) {this.cardId cardId;this.money money;}// 小明 小红同时过来的public void drawMoney(double money) {// 先搞清楚是谁来取钱String name Thread.currentThread().getName();// 1、判断余额是否足够if(this.money money){System.out.println(name 来取钱 money 成功);this.money - money;System.out.println(name 来取钱后余额剩余 this.money);}else {System.out.println(name 来取钱余额不足~);}}public String getCardId() {return cardId;}public void setCardId(String cardId) {this.cardId cardId;}public double getMoney() {return money;}public void setMoney(double money) {this.money money;} } 在定义一个取钱的线程类 public class DrawThread extends Thread{private Account acc;public DrawThread(Account acc, String name){super(name);this.acc acc;}Overridepublic void run() {// 取钱(小明小红)acc.drawMoney(100000);} } 最后再写一个测试类在测试类中创建两个线程对象 public class ThreadTest {public static void main(String[] args) {// 1、创建一个账户对象代表两个人的共享账户。Account acc new Account(ICBC-110, 100000);// 2、创建两个线程分别代表小明 小红再去同一个账户对象中取钱10万。new DrawThread(acc, 小明).start(); // 小明new DrawThread(acc, 小红).start(); // 小红} } 执行结果 某个执行结果: 小明来取钱100000.0成功 小红来取钱100000.0成功 小红来取钱后余额剩余-100000.0 小明来取钱后余额剩余0.0线程同步 概述 线程同步解决线程安全问题的方案。 线程同步的思想 让多个线程实现先后依次访问共享资源这样就解决了安全问题。 线程同步的常见方案 加锁每次只允许一个线程加锁加锁后才能进入访问访问完毕后自动解锁然后其他线程才能再加锁进来。 Java提供了三种方案: 同步代码块同步方法Lock锁 1. 同步代码块 作用把访问共享资源的核心代码给上锁以此保证线程安全。 synchronized(同步锁{访问共享资源的核心代码 }原理每次只允许一个线程加锁后进入执行完毕后自动解锁其他线程才可以进来执行。 同步锁的注意事项 对于当前同时执行的线程来说同步锁必须是同一把同一个对象否则会出bug。 代码示例 在共享账户类里使用同步代码块来解决前面代码里面的线程安全问题。我们只需要修改Account类中的代码即可。 // 小明 小红线程同时过来的 public void drawMoney(double money) {// 先搞清楚是谁来取钱String name Thread.currentThread().getName();// 1、判断余额是否足够// this表示该账户对象正好代表共享资源synchronized (this) {if(this.money money){System.out.println(name 来取钱 money 成功);this.money - money;System.out.println(name 来取钱后余额剩余 this.money);}else {System.out.println(name 来取钱余额不足~);}} }执行结果 小明来取钱100000.0成功 小明来取钱后余额剩余0.0 小红来取钱余额不足~锁对象如何选择的问题 1. 建议把共享资源作为锁对象, 不要将随便无关的对象当做锁对象 我们把锁改为锁 这样一个字符串也行 因为这个资源在内存中永远只有一份 所以各个线程需要去竞争 但是这样好不好 明显不行 万一有另外俩个人再创了一个账户 那就变成了四个人竞争一把锁了 2. 对于实例方法建议使用this作为锁对象 3. 对于静态方法建议把类的字节码(类名.class)当做锁对象这里是Account.class2. 同步方法 同步方法就是把整个方法给锁住一个线程调用这个方法另一个线程调用的时候就执行不了只有等上一个线程调用结束下一个线程调用才能继续执行同样是修改Account类中的代码即可。 修饰符 synchronized 返回值类型 方法名称(形参列表{操作共享资源的代码 }原理每次只能一个线程进入执行完毕以后自动解锁其他线程才可以进来执行。 同步方法底层原理 同步方法其实底层也是有隐式锁对象的只是锁的范围是整个方法代码。如果方法是实例方法同步方法默认用this作为的锁对象如果方法是静态方法同步方法默认用类名.class作为的锁对象。 示例代码如下 // 同步方法 public synchronized void drawMoney(double money) {// 先搞清楚是谁来取钱String name Thread.currentThread().getName();// 1、判断余额是否足够if(this.money money){System.out.println(name 来取钱 money 成功);this.money - money;System.out.println(name 来取钱后余额剩余 this.money);}else {System.out.println(name 来取钱余额不足~);} } 同步代码块和同步方法区别 1.不存在哪个好与不好只是一个锁住的范围大一个范围小 其中锁的范围小一点 性能稍微好一点 可以提前加载那些公共区域的代码 但是提升的性能对于现在的计算机来说可以忽略不计 反而同步方法的可读性要好一些 2.同步方法是将方法中所有的代码锁住 3.同步代码块是将方法中的部分代码锁住3. Lock锁 Lock锁是JDK5开始提供的一个新的锁定操作通过它可以创建出锁对象进行加锁和解锁更灵活、更方便、更强大。 Lock是接口不能直接实例化可以采用它的实现类ReentrantLock来构建Lock锁对象。 Lock锁是JDK5版本专门提供的一种锁对象通过这个锁对象的方法来达到加锁和释放锁的目的使用起来更加灵活。格式如下 1.首先在成员变量位置需要创建一个Lock接口的实现类对象这个对象就是锁对象private final Lock lk new ReentrantLock(); 2.在需要上锁的地方加入下面的代码lk.lock(); // 加锁//...中间是被锁住的代码...lk.unlock(); // 解锁使用Lock锁改写前面DrawThread中取钱的方法代码如下 // 创建了一个锁对象 //因为俩个线程公用一个账户 所以建立一个实例变量作为锁是可以的 //用final修饰更专业 防止二次赋值 private final Lock lk new ReentrantLock();public void drawMoney(double money) {// 先搞清楚是谁来取钱String name Thread.currentThread().getName();try {//用try cath finally写更专业 因为你不能保证被锁的代码没有bug 有bug也要及时解锁lk.lock(); // 加锁// 1、判断余额是否足够if(this.money money){System.out.println(name 来取钱 money 成功);this.money - money;System.out.println(name 来取钱后余额剩余 this.money);}else {System.out.println(name 来取钱余额不足~);}} catch (Exception e) {e.printStackTrace();} finally {lk.unlock(); // 解锁}} }运行程序结果观察线程安全问题已解决。 注意事项 lock锁需要使用final修饰更专业 防止二次赋值 private final Lock lk new ReentrantLock(); 加锁和解锁时用try cath finally写更专业 因为你不能保证被锁的代码没有bug 有bug也要及时解锁 线程通信 概述 什么是线程通信 当多个线程共同操作共享的资源时线程间通过某种方式互相告知自己的状态以相互协调并避免无效的资源争夺。 线程通信的常见模型生产者与消费者模型 生产者线程负责生产数据消费者线程负责消费生产者生产的数据。注意生产者生产完数据应该等待自己通知消费者消费消费者消费完数据也应该等待自己再通知生产者生产 线程通信案例 比如下面案例中有3个厨师生产者线程两个顾客消费者线程。 案例的思路 1.先确定在这个案例中什么是共享数据答这里案例中桌子是共享数据因为厨师和顾客都需要对桌子上的包子进行操作。2.再确定有那几条线程哪个是生产者哪个是消费者答厨师是生产者线程3条生产者线程 顾客是消费者线程2条消费者线程3.什么时候将哪一个线程设置为什么状态生产者线程(厨师)放包子1)先判断是否有包子2)没有包子时厨师开始做包子, 做完之后把别人唤醒然后让自己等待3)有包子时不做包子了直接唤醒别人、然后让自己等待消费者线程(顾客)吃包子1)先判断是否有包子2)有包子时顾客开始吃包子, 吃完之后把别人唤醒然后让自己等待3)没有包子时不吃包子了直接唤醒别人、然后让自己等待 注意上述方法应该使用当前同步锁对象进行调用。 释放当前锁对象时必须先唤醒其他线程再释放自己所占锁 案例代码实现 按照上面分析的思路和java Object提供的api写代码。先写桌子类代码如下 public class Desk {private ListString list new ArrayList();// 放1个包子的方法// 厨师1 厨师2 厨师3//实例方法默认用this作为锁 所以可以保证锁住5个线程 它们公用一个桌子对象//锁也是可以跨方法的public synchronized void put() {try {String name Thread.currentThread().getName();// 判断是否有包子。if(list.size() 0){list.add(name 做的肉包子);System.out.println(name 做了一个肉包子~~);Thread.sleep(2000);//让程序跑慢点容易观察// 唤醒别人, 等待自己this.notifyAll();//必须用当前同步锁对象进行调用 否则会出bugthis.wait();//因为只有锁对象知道当前谁占据着它 谁需要等待}else {// 有包子了不做了。// 唤醒别人, 等待自己this.notifyAll();//注意notifyAll()和wait()位置不能调换this.wait();//你如果先wait了 你让自己等待了 那你还怎么唤醒别人}} catch (Exception e) {//拦截sleep异常e.printStackTrace();}}// 吃货1 吃货2public synchronized void get() {try {String name Thread.currentThread().getName();if(list.size() 1){// 有包子吃了System.out.println(name 吃了 list.get(0));list.clear();Thread.sleep(1000);this.notifyAll();this.wait();}else {// 没有包子this.notifyAll();this.wait();}} catch (Exception e) {e.printStackTrace();}} } 再写测试类在测试类中创建3个厨师线程对象再创建2个顾客对象并启动所有线程 public class ThreadTest {public static void main(String[] args) {// 需求3个生产者线程负责生产包子每个线程每次只能生产1个包子放在桌子上// 2个消费者线程负责吃包子每人每次只能从桌子上拿1个包子吃。Desk desk new Desk();// 创建3个生产者线程3个厨师new Thread(() - {//匿名内部类写法while (true) {desk.put();}}, 厨师1).start();new Thread(() - {while (true) {desk.put();}}, 厨师2).start();new Thread(() - {while (true) {desk.put();}}, 厨师3).start();// 创建2个消费者线程2个吃货new Thread(() - {while (true) {desk.get();}}, 吃货1).start();new Thread(() - {while (true) {desk.get();}}, 吃货2).start();} } 执行结果如下: 厨师1做了一个肉包子~~ 吃货2吃了厨师1做的肉包子 厨师3做了一个肉包子~~ 吃货1吃了厨师3做的肉包子 厨师1做了一个肉包子~~ 吃货1吃了厨师1做的肉包子 厨师3做了一个肉包子~~ 吃货1吃了厨师3做的肉包子 厨师1做了一个肉包子~~ 吃货2吃了厨师1做的肉包子 //不终止则一直运行下去 可以发现没有出现线程安全问题 线程池 线程池概述 什么是线程池 线程池就是一个可以复用线程的技术。 不使用线程池的问题 用户每发起一个请求后台就需要创建一个新线程来处理下次新任务来了肯定又要创建新线程处理的而创建新线程的开销是很大的并且请求过多时肯定会产生大量的线程出来这样会严重影响系统的性能。 线程池解决的问题 使用线程池就可以解决上面的问题。线程池内部会有一个容器存储几个核心线程假设有3个核心线程这3个核心线程可以处理3个任务。 但是任务总有被执行完的时候假设第1个线程的任务执行完了那么第1个线程就空闲下来了有新的任务时空闲下来的第1个线程可以去执行其他任务。依此内推这3个线程可以不断的复用也可以执行很多个任务。 所以线程池就是一个线程复用技术它可以提高线程的利用率。 线程池创建 在JDK5版本中提供了代表线程池的接口ExecutorService而这个接口下有一个实现类叫ThreadPoolExecutor类使用ThreadPoolExecutor类就可以用来创建线程池对象。下面是它的构造器参数比较多 用这7个参数的构造器来创建线程池的对象。代码如下 ExecutorService pool new ThreadPoolExecutor(3, //核心线程数有3个5, //最大线程数有5个。 临时线程数最大线程数-核心线程数5-328, //临时线程存活的时间8秒。 意思是临时线程8秒没有任务执行就会被销毁掉。TimeUnit.SECONDS,//时间单位秒new ArrayBlockingQueue(4), //任务阻塞队列没有来得及执行的任务在任务队列中等待Executors.defaultThreadFactory(), //用于创建线程的工厂对象new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略 ); 关于线程池需要注意下面的两个问题 临时线程什么时候创建 注意新任务提交时发现核心线程都在忙、并且任务队列满了、并且还可以创建临时线程此时会创建临时线程。 注意是任务队列满了之后才会创建临时线程 而不是临时线程满了才加入任务队列 什么时候开始拒绝新的任务 核心线程和临时线程都在忙、任务队列也满了、新任务过来时才会开始拒绝任务。 线程池执行Runnable任务 创建好线程池之后接下来我们就可以使用线程池执行任务了。 线程池执行的任务可以有两种一种是Runnable任务一种是callable任务。 下面的execute方法可以用来执行Runnable任务。 代码案例 先准备一个线程任务类 public class MyRunnable implements Runnable{Overridepublic void run() {// 任务是干啥的System.out.println(Thread.currentThread().getName() 输出666~~);//为了模拟线程一直在执行这里睡久一点try {Thread.sleep(Integer.MAX_VALUE);} catch (InterruptedException e) {e.printStackTrace();}} } 线程池处理任务类 执行Runnable任务的代码注意阅读注释对照着前面的7个参数理解。 public class ThreadPoolTest1 {public static void main(String[] args) {// 1、通过ThreadPoolExecutor创建一个线程池对象。ExecutorService pool new ThreadPoolExecutor(3, 5, 8,TimeUnit.SECONDS, new ArrayBlockingQueue(4), Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());Runnable target new MyRunnable();pool.execute(target); // 线程池会自动创建一个新线程自动处理这个任务自动执行的pool.execute(target); // 线程池会自动创建一个新线程自动处理这个任务自动执行的pool.execute(target); // 线程池会自动创建一个新线程自动处理这个任务自动执行的pool.execute(target);pool.execute(target);pool.execute(target);pool.execute(target);// 到了临时线程的创建时机了pool.execute(target);pool.execute(target);// 到了新任务的拒绝时机了pool.execute(target);// pool.shutdown(); // 等着线程池的任务全部执行完毕后再关闭线程池// pool.shutdownNow(); // 立即关闭线程池不管任务是否执行完毕} }执行结果: pool-1-thread-5 输出666~~ main 输出666~~ pool-1-thread-1 输出666~~ pool-1-thread-3 输出666~~ pool-1-thread-4 输出666~~ pool-1-thread-2 输出666~~ //其中123是核心线程执行的 45是临时线程执行的 //注意程序还是一直运行的 线程池不会自动关闭 设计出来就是一直服务的 //main输出是因为使用了CallerRunsPolicy()拒绝策略 新来的要拒绝的任务由主线程main执行了 线程池执行Callable任务 Callable任务相对于Runnable任务来说就是多了一个返回值。 执行Callable任务需要用到上面ExecutorService的submit方法 代码案例 先准备一个Callable线程任务 public class MyCallable implements CallableString {private int n;public MyCallable(int n) {this.n n;}// 2、重写call方法Overridepublic String call() throws Exception {// 描述线程的任务返回线程执行返回后的结果。// 需求求1-n的和返回。int sum 0;for (int i 1; i n; i) {sum i;}return Thread.currentThread().getName() 求出了1- n 的和是 sum;} } 再准备一个测试类在测试类中创建线程池并执行callable任务。 public class ThreadPoolTest2 {public static void main(String[] args) throws Exception {// 1、通过ThreadPoolExecutor创建一个线程池对象。ExecutorService pool new ThreadPoolExecutor(3,5,8,TimeUnit.SECONDS, new ArrayBlockingQueue(4),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());// 2、使用线程处理Callable任务。FutureString f1 pool.submit(new MyCallable(100));FutureString f2 pool.submit(new MyCallable(200));FutureString f3 pool.submit(new MyCallable(300));FutureString f4 pool.submit(new MyCallable(400));// 3、执行完Callable任务后需要获取返回结果。System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());System.out.println(f4.get());} } 某次执行后结果如下所示 pool-1-thread-1求出了1-100的和是5050 pool-1-thread-2求出了1-200的和是20100 pool-1-thread-3求出了1-300的和是45150 pool-1-thread-3求出了1-400的和是80200核心线程数量到底应该配置多少呢 根据经验法则大致参考以下原则: 如果是计算密集型的任务核心线程数量 CPU的核数 1如果是IO密集型的任务核心线程数量 CPU核数 * 2 CPU核数查看这个cpu是16核。 线程池工具类Executors Executors是一个线程池的工具类提供了很多静态方法用于返回不同特点的线程池对象。 注意这些方法的底层都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象。 测试代码 public class ThreadPoolTest3 {public static void main(String[] args) throws Exception {// 1、通过Executors创建一个线程池对象。ExecutorService pool Executors.newFixedThreadPool(17);// 2、使用线程处理Callable任务。FutureString f1 pool.submit(new MyCallable(100));FutureString f2 pool.submit(new MyCallable(200));FutureString f3 pool.submit(new MyCallable(300));FutureString f4 pool.submit(new MyCallable(400));System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());System.out.println(f4.get());} } Executors创建线程池这么好用为什么不推荐同学们使用呢原因在这里看下图这是《阿里巴巴Java开发手册》提供的强制规范要求在大型并发系统环境中容易出bug。 并发、并行和生命周期 并发和并行 1. 什么是进程、线程 正常运行的程序软件就是一个独立的进程线程是属于进程一个进程中包含多个线程进程中的线程其实并发和并行同时存在 可以打开系统的任务管理器看看快捷键CtrlShfitEsc自己的电脑上目前有哪些进程。 2. 什么是并发 进程中的线程由CPU负责调度执行但是CPU同时处理线程的数量是有限的为了保证全部线程都能执行到CPU采用轮询机制为系统的每个线程服务由于CPU切换的速度很快给我们的感觉这些线程在同时执行这就是并发。 简单记并发就是多条线程交替执行 3. 什么是并行 并行指的是多个线程同时被CPU调度执行。如下图所示多个CPU核心在执行多条线程 4. 多线程到底是并发还是并行呢 其实多个线程在我们的电脑上执行并发和并行是同时存在的。 线程的生命周期 在Thread类中有一个嵌套的枚举类叫Thread.Status这里面定义了线程的6中状态。如下图所示 NEW: 新建状态线程还没有启动 RUNNABLE: 可以运行状态线程调用了start()方法后处于这个状态 BLOCKED: 锁阻塞状态没有获取到锁处于这个状态 WAITING: 无限等待状态线程执行时被调用了wait方法处于这个状态 TIMED_WAITING: 计时等待状态线程执行时被调用了sleep(毫秒)或者wait(毫秒)方法处于这个状态 TERMINATED: 终止状态, 线程执行完毕或者遇到异常时处于这个状态。 这几种状态之间切换关系如下图所示 线程的六种状态的总结 乐观锁与悲观锁 悲观锁:一上来就加锁没有安全感每次只能一个线程进入访问完毕后再解锁。是线程安全的但是性能较差 乐观锁:一开始不上锁认为是没有问题的大家一起跑等要出线程安全问题的时候才开始控制。是线程安全的且性能较好。 下面举例说明先写一个没有锁的多线程场景 public static void main(String[] args) throws Exception {//需求:1个静态变量100个线程每个线程对其加100次 最终值为10000Runnable target new MyRunnable();for (int i 1; i 100; i) {new Thread(target).start();}} public class MyRunnable implements Runnable{private int count;//用实例变量代替静态变量 反正线程都是公用一个任务对象的 所以是可以的Overridepublic void run() {//100次for (int i 0; i 100; i) {System.out.println(count---------(count));}} } 某次运行结果 ... count---------9989 count---------9990 count---------9991 count---------9992 count---------9993 count---------9994 count---------9995 count---------9996 count---------9997 count---------9998 count---------9999 //虽然概率比较小 还是出现了一次线程安全问题 //有一次增值计算重叠了 没有加到10000乐观锁解决 首先解释原理安全问题来自于比如当count是10时俩个线程几乎同时进入将其值修改成11于是便发生了安全问题少加了一次。乐观锁采用CAS算法(可以自己进入count.incrementAndGet()源码看看)在加之前就记录了count的原来的值比如当线程进入时记录count是10然后将其加到11准备写入时发现count已经变成11了于是会将这次修改写入作废重复上述过程重新加一次。 代码如下 public class MyRunnable implements Runnable{//整数修改的乐观锁:用java的原子类实现的private AtomicInteger count new AtomicInteger();Overridepublic void run() {//100次for (int i 0; i 100; i) {System.out.println(count---------(count.incrementAndGet()));}} }执行结果可以发现没有线程安全问题。
http://www.tj-hxxt.cn/news/233550.html

相关文章:

  • 利用别人域名做自己的网站seo是什么意思som
  • 万维网网站备案流程生态文明建设
  • 盐城网站建设建站wordpress七牛同步上传
  • 公司做网站要多长时间审核phpcms仿站
  • 济南公司建站城乡建设厅官方网站办事大厅
  • 12388网站建设管理情况做自适应网站制作
  • 学做网站好做吗沈阳 教育 公司 网站建设
  • 网站改版提交wordpress居中代码
  • 做系统正版win10系统下载网站开发公司与物业公司合同
  • 江苏建设主管部门网站更改wordpress程序站点网址
  • 网站返回按钮设计昆明网络营销线上广告
  • 网站上的广告怎么做网络营销成功的品牌
  • 用jsp做的网站有哪些兰州市网站
  • 做网站有名的公司服务器网站配置
  • pc营销型网站上海装修公司排名391
  • 电子商务网站建设具体方案wordpress 自定义feed
  • 济南做网站公司xywlcn网站建设实训分析总结
  • 亳州建设网站公司2010年4月江苏省03340网站建设与管理答案
  • wap网站用什么服务器做网站简单还是写程序
  • 那个网站可以找人做设计python语言入门
  • 网站建设的电话销售好做不建网站的几个公司
  • 雅虎网站提交网站建设费用如何做账务处理
  • 阿里巴巴官网网站快递公司网站怎么做
  • 网站根目录在哪儿广告设计主要学什么
  • 做的比较好的二手交易网站商丘网络推广平台
  • 企业自助建站源码wordpress分享跳转插件
  • 网站开发财务毕业设计网站开发的中期报告
  • 免费做网站软件下载有内涵大气的公司名字
  • html中网站最下面怎么做中铝长城建设有限公司网站
  • 为什么要做手机网站网站做多个产品