1.进程
是一个独立运行的应用程序
比如电脑软件,启动之后,就是一个进程,需要系统进行的分配,可以获取当前系统内存,网卡,显卡等。
1.独立性
各个进程之间是互相独立的,互不影响的。
2.互斥性
每个应用程序(软件) 系统分配一个独立的端口号
写代码 Demo1 里面 main主函数 Demo1可以看成一个应用程序 就是一个进程
2.线程
进程是由至少一个或者多个线程组成的。
线程是进程最小的基本单位。
特性: 1.抢占式运行【重要】 给程序 分配CPU,按照时间片来执行,单位时间片抢占式执行的。随机抢占的 2.资源共享 同一个进程,有多个线程,这个多个线程是可以共享同一个数据的 Java程序:Demo1可以看成一个进程,一个Java程序中有两个线程: 1.main 主线程 2.垃圾回收的线程
面试题:进程和线程的区别
1、调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。 2、并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行。 3、拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程 的资源。 4、系统开销:在创建或撤销进程的时候,由于系统都要为之分配和回收资源,导致系统的明显 大于创建或撤销线程时的开销。但进程有独立的地址空间,进程崩溃后,在保护模式下不会对其 他的进程产生影响,而线程只是一个进程中的不同的执行路径。线程有自己的堆栈和局部变量, 但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多 线程的程序健壮,但是在进程切换时,耗费的资源较大,效率要差些。
3.并行和并发
并行:真正的同时执行
并发:同时发生,轮流交替执行
4.创建线程的两种方式
Java虚拟机允许应用程序同时执行多个执行线程。
创建线程的第一种:
创建一个新的执行线程有两种方法。一个是将一个类声明为Thread的子类(继承Thread)。这个子类应该重写run方法,然后可以分配并启动子类的实例。
package com.qf.Day23;
class MyThread1 extends Thread {
//重写run方法
@Override
public void run() {
//这个线程中打印10次的MyThread1线程:
for (int i = 0; i < 9; i++) {
System.out.println("线程:" + i);
}
}
}
public class Demo4 {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
//启动线程
new Thread(myThread1).start();
for (int i = 0; i < 100; i++) {
System.out.println("main主线程" + i);
}
}
}
练习:新建两个线程,一个县城打印99乘法表,另一个线程打印直角三角形
package com.qf.a_thread;
class MyThread3 extends Thread {
@Override
public void run() {
for (int i = 1; i < 10; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(j + "*" + i + "=" + i*j + "\t");
}
System.out.println();
}
}
}
class MyThread4 extends Thread {
@Override
public void run() {
for (int i = 1; i < 10 ; i++) {
for (int j = 1; j <= i; j++) {
System.out.print("*");
}
System.out.println();
}
}
}
public class Demo2 {
public static void main(String[] args) {
new MyThread3().start();
new MyThread4().start();
}
}
第二种创建方式:
另一种方法来创建一个线程是声明实现类Runnable接口。 那个类然后实现了run方法。 然后可以分配类的实例,在创建Thread时作为参数传递,并启动。
package com.qf.b_thread;
class MyThread1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("MyThread1:" + i);
}
}
}
class MyThread2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("MyThread2:" + i);
}
}
}
public class Demo1 {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
MyThread2 myThread2 = new MyThread2();
//在创建Thread时作为参数传递并启动。
new Thread(myThread1).start();
new Thread(myThread2).start();
}
}
练习:新建两个线程,一个线程打印99乘法表,另外一个线程打印直接三角形
package com.qf.b_thread;
class MyThread3 implements Runnable {
@Override
public void run() {
for (int i = 1; i < 10 ; i++) {
for (int j = 1; j <= i; j++) {
System.out.print("*");
}
System.out.println();
}
}
}
class MyThread4 implements Runnable {
@Override
public void run() {
for (int i = 1; i < 10; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(j + "*" + i + "=" + i*j + "\t");
}
System.out.println();
}
}
}
public class Demo3 {
public static void main(String[] args) {
//然后可以分配类的实例,在创建Thread时作为参数传递,并启动。
new Thread(new MyThread3()).start();
new Thread(new MyThread4()).start();
}
}
5.死锁
开发中禁止出现死锁
面试题:
应用场景:并发场景,多个线程。线程之间在共享数据的时候是互不相让的
线程加锁是为了线程安全
死锁是一种状态,当两个线程互相持有对象所需要的资源的时候,这两个线程又都不主动释放资源。
就会导致死锁。代码无法正常执行。这两个线程就会僵持住。
package com.qf.review;
class DeadLock implements Runnable {//实现Runnable接口
private boolean flag;//标记
private Object obj1;//对象1
private Object obj2;//对象2
public DeadLock(boolean flag, Object obj1, Object obj2) {//构造函数
this.flag = flag;
this.obj1 = obj1;
this.obj2 = obj2;
}
//重写run方法
@Override
public void run() {
if (flag) {//如果flag = true 让线程1执行这个if语句里面的代码
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + "拿到了obj1资源");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1执行了");
synchronized (obj2) {//锁obj2的前提是先有obj2资源
//想用obj2这个资源,但是下面未释放obj1,所以就停了,导致obj2也未释放掉
System.out.println(Thread.currentThread().getName() + "拿到obj2资源");
}
}
}
if (!flag) {//如果flag=false 线程2 执行这个if语句里面的代码
synchronized (obj2) {
System.out.println(Thread.currentThread().getName() + "拿到了obj2资源");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2执行了");
synchronized (obj1) {//不自动释放
//想用obj1这个资源,但是上面未释放obj2,所以就停了,导致obj1也未释放掉
System.out.println(Thread.currentThread().getName() + "拿到obj1资源");
}
}
}
}
}
public class Review20 {
public static void main(String[] args) {
Object obj1 = new Object();
Object obj2 = new Object();
DeadLock deadLock1 = new DeadLock(true, obj1, obj2);
new Thread(deadLock1, "线程1").start();
DeadLock deadLock2 = new DeadLock(false, obj1, obj2);
new Thread(deadLock2, "线程1").start();
}
}
6.Object类下面的和线程相关的方法
public final void wait(){ throws InterruptedException }
导致当前线程等待,直到另一个线程调用该对象notify()方法或者notifyAll()方法。
总结:至少两个线程,对象.wait(),那么当前线程就会阻塞。阻塞就是这个线程暂时不执行了。如何让这个线程继续执行下去呢?再开另外一个线程,对象.notify()去唤醒另外一个线程!!!
wait1线程正在等待中....
唤醒线程已经执行
wait1线程被唤醒!!!Line45行的
我是修改之后的message对象
wait2线程正在等待中....
package com.qf.review;
class Message{//先创建类,再创建对象,再调用wait方法
private String message;
public Message(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
//线程类
class WaitThread implements Runnable {
private Message message;//使用message这个对象,调用wait方法 message.wait()
public WaitThread(Message message) {//构造方法
this.message = message;
}
//等待线程抢到了
//等待线程睡了5秒 然后唤醒线程执行。 synchronized (message)
//message 对象从等待池中国去取的,结果发现没有 阻塞
//回到等待线程睡醒了以后开始 wait等待
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + "正在等待中....");
synchronized (message) {
try {
//当调用wait方法的时候,synchronized会自动释放锁,(并将对象放到等待池中),让唤醒线程锁来操作这个对象,然后message对象会放到等待池中
//
message.wait();//代码走到这一步 当前线程会等待!!!直到被唤醒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "被唤醒!!!Line45行的");
System.out.println(message.getMessage());
}
}
}
//唤醒线程
class NotifyThread implements Runnable {
private Message message;
public NotifyThread(Message message ) {
this.message = message;
}
@Override public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("唤醒线程已经执行");
synchronized (message) {
message.setMessage("我是修改之后的message对象");
//message.notify();
message.notifyAll();
}
}
}
public class Review21 {
public static void main(String[] args) {
Message message = new Message("我是message对象");
WaitThread waitThread = new WaitThread(message);
WaitThread waitThread1 = new WaitThread(message);
NotifyThread notifyThread = new NotifyThread(message);
new Thread(waitThread1, "wait2线程").start();
new Thread(waitThread, "wait1线程").start();
new Thread(notifyThread, "notify线程").start();
}
}
总结:
新建两个线程:
一个是等待线程:
等待线程中代码从上往下执行,但是使用object.wait()方法以后,咱们当前的线程就等待了,到wait之后代码暂时不会往下执行了,需要借助于另外一个线程进行唤醒。
一个是唤醒线程:
唤醒线程使用object.notify方法去讲等待线程中的正在等待的线程进行唤醒,让等待线程继续往下执行。
wait和notify方法,实现线程之间的通信问题。
wait2线程正在等待中....
wait1线程正在等待中....
唤醒线程已经执行
wait1线程被唤醒!!!Line45行的
我是修改之后的message对象
wait2线程被唤醒!!!Line45行的
我是修改之后的message对象
7.join方法【开发不用】
为啥join方法放在wait方法之后讲,因为join方法的底层是wait()方法
作用:让主线程等待,一直等待他的子线程执行完以后,才执行主线程
package com.qf;
//创建子线程类
class MyTh implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
String name = Thread.currentThread().getName();
System.out.println(name + "线程执行了");
}
}
}
public class Demo24 {
public static void main(String[] args) throws InterruptedException {
/* MyTh myTh=new MyTh();
Thread thread=new Thread(myTh);*/
Thread thread=new Thread(new MyTh());
thread.start();
thread.join();//让主线程等待,一直等待他的子线程执行完以后,才执行主线程,秒数在join中是无用的
for (int i = 0; i < 100; i++) {
String name=Thread.currentThread().getName();
System.out.println(name + "线程执行了");
}
}
}
主线程和子线程是相对而言的。
package com.qf.Day24;
class FatherTherad implements Runnable{
@Override
public void run() {
//SonThread线程是在FatherTherad线程中启动的,那就意味着SonThread是FatherTherad的子线程
Thread thread = new Thread(new SonThread());
thread.start();
//thread的父线程等待,先执行子线程
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 1000; i++) {//join要先进行操作
System.out.println("父线程" + i);
}
}
}
class SonThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.err.println("子线程" + i);
}
}
}
public class Demo2 {
public static void main(String[] args) {
//在父线程中只需要
//互相套着调用
new Thread(new FatherTherad()).start();
}
}
面试题:如何控两个线程的执行顺序?
1、sleep方法使一个线程先睡会,先执行另外一个线程
2、join,让子线程先执行完,在执行父线程
3、线程池
8.生产者消费者模式
生活中的案例:
卖家:汽车厂商
买家:群众
小明想买一个BYD汉,电车定完以后需要等待。
小明告知汽车厂商我要买车,如果又,直接发货。如果没有,小明会纳入等待状态。等汽车厂商生产好以后,唤醒小明的等待线程。然后等待小明来买车。如果没有人买车。汽车会进入到等待状态。如果有人来买车,立马唤醒汽车厂商线程,让他赶紧造车。
生产者:汽车厂商
消费者:小明
入门案例:
package com.qf.day25;
//生产者消费者模式 Customer Productor
//goods类,两个线程的共享资源
class Goods {//共享资源
private String name;
private double price;
private boolean isProductor;//代表是否需要生产,true 生产 false 表示不需要生产
public Goods(String name, double price, boolean isProductor) {
this.name = name;
this.price = price;
this.isProductor = isProductor;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public boolean isProductor() {
return isProductor;
}
public void setProductor(boolean productor) {
isProductor = productor;
}
}
//消费者线程
class Customer implements Runnable {
private Goods goods;
public Customer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
while (true) {
//上锁
synchronized (goods) {
if (!goods.isProductor()) {//表示不需要生产
System.out.println("消费者购买的是" + goods.getName() + "价格是" + goods.getPrice());
//把标记变成true
goods.setProductor(true);
//唤醒生产者
goods.notify();
} else {//表示的是需要生产,消费者等待
try {
goods.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
class Productor implements Runnable {
private Goods goods;
public Productor(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
int count = 0;
while (true) {
synchronized (goods) {
if (goods.isProductor()) {//true,表示需要生产
//若是奇数的话就生产幻影,若是偶数的话就生产 s9
if (count % 2 == 0) {//偶数
goods.setName("幻影");
goods.setPrice(9.9);
} else {
goods.setName("s9");
goods.setPrice(8.8);
}
count++;
//更改标记
goods.setProductor(false);
//唤醒消费者
goods.notify();
} else {//表示不需要生产
try {
goods.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public class Demo4 {
public static void main(String[] args) {
Goods goods = new Goods("五菱", 6.6, false);
Customer customer = new Customer(goods);
Productor productor = new Productor(goods);
new Thread(customer).start();
new Thread(productor).start();
}
}
goods.notify();
} else {//表示不需要生产
try {
goods.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public class Demo4 {
public static void main(String[] args) {
Goods goods = new Goods(“五菱”, 6.6, false);
Customer customer = new Customer(goods);
Productor productor = new Productor(goods);
new Thread(customer).start();
new Thread(productor).start();
}
}