线程、进程和多线程
Process与Thread
◆说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
◆而进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位
◆通常在一个进程中可以包含若干个线程,当然-一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的的单位。
注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一一个代码,因为切换的很快,所以就有同时执行的错局。
本章核心概念
◆线程就是独立的执行路径;
◆在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
◆main()称之为主线程,为系统的入口,用于执行整个程序;
◆在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的。
◆对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
◆线程会带来额外的开销,如cpu调度时间, 并发控制开销。
◆每个线程在自己的工作内存交互,内存控制不当会造成数据不一致;
线程创建
三种继承方式
Thread class 继承Thread类 (重点)
Runnable 接口 实现Runnable接口(重点)
Callable接口 实现Callable接口(了解)
用户线程 + 守护线程
Thread class
- 自定义线程类继承Thread类;
- 重写run()方法,编写线程执行体;
- 创建线程对象,调用start()方法启用线程;
package Dome;//创建线程方式一: 继承Thread类,重写run()方法,调用start()方法开启线程/*** @author dafran* @version 1.0* @date 2020-05-21 16:50*///线程开启也不一定立即执行,线程的调度是CPU调度执行,交叉执行的。public class TestThread01 extends Thread{@Overridepublic void run() {//run方法线程体for (int i = 0; i < 20; i++) {System.out.println("run方法"+i);}}public static void main(String[] args) {//main方法,主线程//创建一个线程对象TestThread01 testThread01 = new TestThread01();//调用start()方法开启线程testThread01.start();for (int i = 0; i < 20; i++) {System.out.println("main方法"+i);}}}
注意:线程开启也不一定立即执行,线程的调度是CPU调度执行,交叉执行的。
实现Runnable
- 定义MyRunnable类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
推荐使用Runnable对象,因为Java单继承的局限性
package Dome;//创建线程方式二:实现runnable接口,重写run()方法,执行线程需要丢入runnable接口实现类,调用start()方法./*** @author dafran* @version 1.0* @date 2020-05-21 19:52*/public class TestThread02 implements Runnable {@Overridepublic void run() {//run方法线程体for (int i = 0; i < 20; i++) {System.out.println("run方法"+i);}}public static void main(String[] args) {//创建runnable接口实现类对象TestThread02 testThread02 = new TestThread02();//创建线程对象,通过线程对象来开启我们的线程,代理;/*Thread thread = new Thread(testThread02);thread.start();*/new Thread(testThread02).start();for (int i = 0; i < 20; i++) {System.out.println("main方法"+i);}}}
小结:
◆继承Thread类
◆子类继承Thread类具备多线程能力◆启动线程:子类对象. start()◆不建议使用:避免OOP单继承局限性
◆实现Runnable接口
◆实现接口Runnable具有多线程能力◆启动线程:传入目标对象+ Thread对象.start()◆推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
package Dome;//多个线程同时操作一个对象//火车票为例/*** @author dafran* @version 1.0* @date 2020-05-22 17:27*/public class TestThread03 implements Runnable {//票数private int ticketNums = 10;@Overridepublic void run() {while (true) {if (ticketNums<=0){break;}System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- + "票");}}public static void main(String[] args) {TestThread03 ticket = new TestThread03();new Thread(ticket,"dafran").start();new Thread(ticket,"cola").start();new Thread(ticket,"6b92d6").start();}}
package Dome;// 模拟龟兔赛跑import static java.lang.Thread.sleep;/*** @author dafran* @version 1.0* @date 2020-05-22 21:38*/public class Race implements Runnable {//胜利者private static String winner;@Overridepublic void run() {for (int i = 0; i <= 100; i++) {//模拟兔子睡觉if(Thread.currentThread().getName().equals("兔子") && i%10 == 0){try {sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}//判断比赛是否结束boolean flag = gameOver(i);//如果比赛结束了,就停止程序if (flag){break;}System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "步");}}//判断是否完成比赛private boolean gameOver(int steps) {//判断是否有胜利者if (winner != null) {return true;}{if (steps >= 100) {winner =Thread.currentThread().getName();System.out.println("winner is" + winner);return true;}}return false;}public static void main(String[] args) {Race race = new Race();new Thread(race,"兔子").start();new Thread(race,"乌龟").start();}}
实现Callable接口(了解即可)
- 实现Callable接口, 需要返回值类型
- 重写call方法, 需要抛出异常
- 创建目标对象
- 创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);5.提交执行:Futureresult1 = ser.submit(t1);
- 获取结果: boolean r1 = result1.get()
- 关闭服务: ser.shutdownNow();
package Dome;import java.util.concurrent.*;//线程创建方式三:实现callable接口/*callable的好处1.可以定义返回值2.可以抛出异常*//*** @author dafran* @version 1.0* @date 2020-05-23 18:45*///实现Callable接口, 需要返回值类型public class TestCallable implements Callable<Boolean> {//票数private int ticketNums = 10;//重写call方法, 需要抛出异常@Overridepublic Boolean call() throws Exception {while (true) {if (ticketNums<=0){break;}System.out.println(TestCallable.this.ticketNums + "-->拿到了第" + ticketNums-- + "票");}return true;}public static void main(String[] args) throws ExecutionException, InterruptedException {//创建目标对象TestCallable ticket1 = new TestCallable();TestCallable ticket2 = new TestCallable();TestCallable ticket3 = new TestCallable();//创建执行服务ExecutorService ser = Executors.newFixedThreadPool(3);//提交执行Future<Boolean> r1 = ser.submit(ticket1);Future<Boolean> r2 = ser.submit(ticket2);Future<Boolean> r3 = ser.submit(ticket3);System.out.println(r1);System.out.println(r2);System.out.println(r3);//获取结果boolean rs1 = r1.get();boolean rs2 = r2.get();boolean rs3 = r3.get();//关闭服务ser.shutdownNow();}}
静态代理
package Dome;/*静态代理模式总结:真实对象和代理对象都要实现同一个接口代理对象要代理真实角色*//*好处:代理对象可以做很多真实对象做不了的事情真实对象专注做自己的事仿*//*** @author dafran* @version 1.0* @date 2020-05-23 20:07*/public class StaticProxy {public static void main(String[] args) {//类比 Thread 利用了 静态代理/**new Thread(()-> System.out.println("loveYou")).start();new WeddingCompany(new You()).HappyMarry(); */WeddingCompany weddingCompany = new WeddingCompany(new You());weddingCompany.HappyMarry();}}interface Marry {void HappyMarry();}class You implements Marry {@Overridepublic void HappyMarry() {System.out.println("淦,老子结婚了");}}class WeddingCompany implements Marry {private Marry target;public WeddingCompany(Marry target) {this.target = target;}@Overridepublic void HappyMarry() {before();this.target.HappyMarry();after();}private void before() {System.out.println("布置婚礼现场");}private void after() {System.out.println("收拾现场,收钱");}}
Lamda表达式
- λ希腊字母表第十一个字母,英文Lambda
- 避免匿名内部类定义过多
- 其实质属于函数式编程的概念
(params) -> experssin [表达式] (params) -> statement[语句] (params) -> {statement}
a -> System.out.println("This is Lambda -->"+a);new Thread (() -> System.out.println("多线程的学习")) .start();
- 为什么要使用lambda表达式
- 避免匿名内部类定义过多。
- 去掉了-堆没有意义的代码,只留下核心的逻辑。
- 可以让你的代码看起来很简洁。
- 也许你会说,我看了Lambda表达式,不但不觉得简洁,反而觉得更乱,看不懂了。那是因为我们还没有习惯, 用的多了,看习惯了,就好了。
Lamda表达式 :
- 理解Functional lnterface (函数式接口) 是学习Java8 lambda表达式的关键所在。
函数式接口的定义:
- 任何借口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
public interface Runnable {public abstract void run();}
- 任何借口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。
package Dome;/*推导lambda表达式*//*** @author dafran* @version 1.0* @date 2020-05-24 17:18*/public class TestLambda01 {//3.静态内部类static class Like2 implements ILike {@Overridepublic void lambda() {System.out.println("This is Lambda2");}}public static void main(String[] args) {ILike like = new Like();like.lambda();like = new Like2();like.lambda();//4.局部内部类class Like3 implements ILike {@Overridepublic void lambda() {System.out.println("This is Lambda3");}}like = new Like3();like.lambda();//5.匿名内部类,没有类的名称,必须借助接口或者父类like = new ILike() {@Overridepublic void lambda() {System.out.println("This is Lambda4");}};like.lambda();//6.用Lambda简化like = ()->{System.out.println("This is Lambda5");};like.lambda();}}//1.定义一个函数式接口interface ILike{void lambda();}//2.实现类class Like implements ILike {@Overridepublic void lambda() {System.out.println("This is Lambda");}}
package Dome;/*** @author dafran* @version 1.0* @date 2020-05-24 17:47*/public class TestLambda02{public static void main(String[] args) {ILove love = null;//1.Lambda简化love = (int a) ->{System.out.println("This is Love->" + a);};//2.参数简化love = (a) ->{System.out.println("This is Love->" + a);};//3.简化括号love = a -> {System.out.println("This is Love->" + a);};//4.简化花括号love = a -> System.out.println("This is Love->" + a);// 总结:// lambda表达式只能有一行代码的情况下才能简化成为行,如果有多行, 那么就用代码块包// 前提是接口为函数式接口// 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号love.love(520);}}interface ILove{void love(int a);}
线程的状态
| 方法 | 说明 |
|---|---|
| setPriority(int newPriority) | 更改线程的优先级 |
| static void sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程休眠 |
| void join() | 等待该线程终止 |
| static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
| void interrupt() | 中断线程,别用这个方式 |
| boolean isAlive() | 测试线程是否处于活动状态 |
停止线程
- 不推荐使用JDK提供的 stop()、destroy()。[已废弃]
- 推荐线程自己停下来
- 建议使用一个标志位进行终止变量,当flag = false, 则终止线程运行。
package Dome;/*** @author dafran* @version 1.0* @date 2020-05-26 15:38*/public class TestStop implements Runnable {//1.设置标识位private boolean flag = true;@Overridepublic void run() {int i = 0;while (flag){System.out.println("run...Thread" + i++);}}//2.设置一个公开的方法停止线程,转换标志位public void stop(){this.flag = false;}public static void main(String[] args){TestStop testStop = new TestStop();new Thread(testStop).start();for (int i = 0; i < 10000; i++) {System.out.println("main"+i);if (i==9000){//调用stop方法testStop.stop();System.out.println("stop");}}}}
线程休眠
- sleep(时间) 指定当前线程阻塞的毫秒数;
- sleep存在异常InterruptedException;
- sleep时间达到后线程进入就绪队列;
- sleep可以模拟网络延时,倒计时等;
- 每一个对象都有一个锁,sleep不会释放锁;
package Dome;/*** @author dafran* @version 1.0* @date 2020-05-26 16:46*/public class TestSleep implements Runnable {//票数private int ticketNums = 10;@Overridepublic void run() {while (true) {if (ticketNums<=0){break;}//模拟网络延时:方法问题的发生性。try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- + "票");}}public static void main(String[] args) {TestSleep ticket = new TestSleep();new Thread(ticket,"dafran").start();new Thread(ticket,"cola").start();new Thread(ticket,"6b92d6").start();}}
package Dome;import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;import java.text.SimpleDateFormat;import java.util.Date;/*** @author dafran* @version 1.0* @date 2020-05-26 16:53*/public class TestSleep02 {public static void main(String[] args) {//打印系统当前时间Date startTime = new Date(System.currentTimeMillis()); //获取当前时间while (true) {try {Thread.sleep(1000);System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));startTime = new Date(System.currentTimeMillis()); //更新当前时间} catch (InterruptedException e) {e.printStackTrace();}}/*try {tenDown();} catch (InterruptedException e) {e.printStackTrace();}*/}public static void tenDown() throws InterruptedException {int num = 10; //倒计时10秒while (true) {Thread.sleep(1000);System.out.println(num--);if (num <= 0) {break;}}}}
线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞;
- 将线程从运行状态转为就绪状态;
- 让CPU重新调度,礼让不一定成功,看CPU心情
package Dome;/*** @author dafran* @version 1.0* @date 2020-05-26 17:28*/public class TestYileld {public static void main(String[] args) {MyYield myYield = new MyYield();new Thread(myYield,"a").start();new Thread(myYield,"b").start();}}class MyYield implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"线程开始执行");Thread.yield(); //礼让System.out.println(Thread.currentThread().getName()+"线程停止执行");}}
合并线程
- Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞。
- 类似插队(VIP)
package Dome;/*** @author dafran* @version 1.0* @date 2020-05-26 20:19*/public class TestJoin implements Runnable {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("VIP高调路过" + i);}}public static void main(String[] args) throws InterruptedException {//启动我们的线程TestJoin testJoin = new TestJoin();Thread thread = new Thread(testJoin);thread.start();//主线程for (int i = 0; i < 500; i++) {if (i == 200){thread.join(); //插队}System.out.println("main"+i);}}}
生产者消费者
package Dome;import java.util.Random;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.TimeUnit;/*** @author dafran* @version 1.0* @date 2020-05-25 19:35*/public class ProducerConsumer{public static void main(String[] args) {// 任务调度器ExecutorService exec = Executors.newFixedThreadPool(10);// 仓库final LinkedBlockingQueue<Integer> buffer = new LinkedBlockingQueue<>(5);for (int i = 0; i < 2; i++) {// 创建生产者Producer p = new Producer(buffer);// 领到把生产者拉到车间,被迫没日没夜的干活exec.execute(p);// 消费者出生了Consumer c = new Consumer(buffer);// 消费者一生都在消费exec.execute(c);}exec.execute(new Runnable() {@Overridepublic void run() {while (true) {// 定时看一下仓库的空间System.out.println("buffer :" + buffer.size());try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}}});}}//消费者class Consumer implements Runnable {LinkedBlockingQueue<Integer> buffer;//注册仓库Consumer(LinkedBlockingQueue<Integer> buffer) {this.buffer = buffer;}/*** 从仓库中的取出产品消费,当仓库里面没有产品时,会一直等下去* @return* @throws InterruptedException*/public Integer consume() throws InterruptedException {Integer e = buffer.take();return e;}@Overridepublic void run() {Random random = new Random(3);try {while (true) {//一生都要吃Integer product = consume();System.out.println(this + " \tConsume:\t " + product);TimeUnit.MILLISECONDS.sleep(random.nextInt(2000));//吃了也要睡会}} catch (InterruptedException e) {e.printStackTrace();}}}//生产者class Producer implements Runnable{LinkedBlockingQueue<Integer> buffer;//构造生产者,注册仓库Producer(LinkedBlockingQueue<Integer> buffer) {this.buffer = buffer;}/*** 生产一个产品,当仓库已经满时,等待仓库有空地再放入仓库* @param e* @throws InterruptedException*/public void produce (Integer e) throws InterruptedException {buffer.put(e);}@Overridepublic void run() {Random random = new Random(3);try {while (true){Integer product = random.nextInt();System.out.println(this + " Priduct: " + product);produce(product);TimeUnit.MILLISECONDS.sleep(random.nextInt(500));//短暂的休息}} catch (InterruptedException e) {e.printStackTrace();}}}
