线程、进程和多线程
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{
@Override
public 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 {
@Override
public 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;
@Override
public 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;
@Override
public 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方法, 需要抛出异常
@Override
public 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 {
@Override
public void HappyMarry() {
System.out.println("淦,老子结婚了");
}
}
class WeddingCompany implements Marry {
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public 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 {
@Override
public 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 {
@Override
public void lambda() {
System.out.println("This is Lambda3");
}
}
like = new Like3();
like.lambda();
//5.匿名内部类,没有类的名称,必须借助接口或者父类
like = new ILike() {
@Override
public 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 {
@Override
public 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;
@Override
public 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;
@Override
public 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 {
@Override
public 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 {
@Override
public 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() {
@Override
public 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;
}
@Override
public 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);
}
@Override
public 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();
}
}
}