1.什么是进程
狭义:进程是正在运行的程序实例
广义:进程是一个具有独立功能的程序关于某个数据集合的一次运行活动,是系统进行资源分配和调度的基本单位
进程的概念要注意两点:
进程是一个实体。每一个进程都有它自己的独立地址空间
进程是一个“执行中的程序”。只有当处理器执行这个程序时,它成为活动的实体
2.什么是线程
线程是进程中的一个实体,是CPU调度的基本单位,只拥有在运行中必不可少的资源,但它可与同属一个进程的其他线程共享所拥有的资源。
3.线程和进程的关系
线程是进程的一个实体,一个进程可以拥有多个线程。
4.进程和线程的区别
(1) 地址空间:同一进程的所有线程共享本进程的地址空间,不同进程之间的地址空间是相互独立的。
(2) 资源拥有:同一进程内的线程共享本进程的资源如:内存、I/O,CPU等,不同的进程资源相互独立。
(3) 健壮性:一个进程崩溃后,不会对其他进程产生影响,但是一个线程崩溃后,会终结整个进程。所以多进程要比多线程健壮。
(4) 线程切换比进程切换消耗的资源更少、速度更快。
(5) 线程是CPU调度的基本单位,进程是系统资源分配的基本单位。
调度问题
同步与异步
并发与并行
并发指两个或多个事件在同一个时间段内发生。
并行指两个或多个事件在同一时刻发生(同时发生)
开启两个线程,使用抢占式的执行方式
他们的执行方式是这样的
Runnable接口
设置和获取线程名称
public class setname {
public static void main(String[] args) {
//如何获取线程的名称
System.out.println(Thread.currentThread().getName());
//两种设置线程名称的方式
Thread t = new Thread(new MyRunnable());
t.setName("wwww");
t.start();
new Thread(new MyRunnable(),"锄禾日当午").start();
//不设置的有默认的名字
new Thread(new MyRunnable()).start();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
}
下边的线程不是固定的,线程是抢占执行的
import java.lang.Thread;
public class THread {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
//两种方式去创建线程
MyRunnable mr = new MyRunnable();
Thread t = new Thread(mr);
t.start();
new Thread(new MyRunnable()).start();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
指定时间休眠Sleep
public class setname {
public static void main(String[] args) {
new Thread(new MyRunnable()).start();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("已经休眠完成");
}
}
}
线程阻塞就是所有消耗时间的操作。如果用stop直接结束线程,那么它没办法及时的把它所拥有的资源去释放
线程的中断(中断只是一个标记,告诉线程该死亡了,然后在线程中添加死亡的方法)
import sun.management.ThreadInfoCompositeData;
public class setname {
public static void main(String[] args) {
MyRunnable r1=new MyRunnable();
Thread t1=new Thread(r1);
t1.start();
t1.interrupt();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//e.printStackTrace();
System.out.println("发现了中断标记该结束了");
return;
}
System.out.println("已经休眠完成");
}
}
}
在实际编程中一般不用stop,stop会强制关闭,如果当时该线程没有释放所拥有的资源会造成麻烦
守护线程
用户线程:当一个进程不包含任何的存活用户线程时,进行结束
守护线程: 守护用户线程的,当最后一个用户线程结束时,所有守护线程自 动死亡
import sun.management.ThreadInfoCompositeData;
public class setname {
public static void main(String[] args) throws InterruptedException {
MyRunnable r1=new MyRunnable();
Thread t1=new Thread(r1);
//设置t1为守护线程,在本例中只有main一个用户线程
t1.setDaemon(true);
t1.start();
for(int i=1;i<5;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
Thread.sleep(1000);
}
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//e.printStackTrace();
System.out.println("发现了中断标记该结束了");
return;
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
在本例中,守护线程应该执行10次,但是当用户线程main执行完成后,就结束了
线程的安全与不安全
public class setname {
public static void main(String[] args) {
//线程不安全
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//总票数
private int count = 10;
@Override
public void run() {
while (count>0){
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("卖票结束,余票:"+count);
}
}
}
}
当余票剩余1 的情况下,A进入catch但是此时丢失了时间片,被B拿到,此时count的值还没来得及改变于是会发生-1张票的情况
线程安全的解决方案一——同步代码块(上锁 ——synchronized,但是上完锁以后效率就变得不高了,谁先抢到,谁更容易抢到接下来的)
三个线程看同一把锁 Object O
//线程同步synchronized
public class setname {
public static void main(String[] args) {
Object o = new Object();
//线程不安全
//解决方案1 同步代码块
//格式:synchronized(锁对象){
//
//
// }
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//总票数
private int count = 10;
private Object o = new Object();
@Override
public void run() {
//Object o = new Object(); //这里不是同一把锁,所以锁不住
while (true) {
synchronized (o) {
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
}else {
break;
}
}
}
}
}
}
可以看到,方法一一旦哪个线程抢到了,那么接下来就会执行很方便
反例,如果Object O放在run里边,那么三个线程在创建的时候都看各自的锁,此时就是线程不安全的
方法二 同步方法
//线程同步synchronized
public class setname {
public static void main(String[] args) {
Object o = new Object();
//线程不安全
//解决方案2 同步方法
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//总票数
private int count = 10;
@Override
public void run() {
while (true) {
boolean flag = sale();
if(!flag){
break;
}
}
}
public synchronized boolean sale(){
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
return true;
}
return false;
}
}
}
需要注意的是这里
如果里边new 三个Ticket
排队的对象是里边创建的这个Ticket,现在里边新创建的,三个,会调用三个run,没有实现排队机制,而之前里边放的是run,三个线程使用他会被锁,实现排队机制,但是上面这种方式不行
方法三——显示锁
一定注意锁的参数,如果是false,那么很大可能被某一个线程独占资源,其他线程得不到资源的分配
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//同步代码块和同步方法都属于隐式锁
//线程同步lock
public class setname {
public static void main(String[] args) {
//线程不安全
//解决方案1 显示锁 Lock 子类 ReentrantLock
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//总票数
private int count = 10;
//参数为true表示公平锁 默认是false 不是公平锁
private Lock l = new ReentrantLock(true);
@Override
public void run() {
while (true) {
l.lock();
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
}else {
break;
}
l.unlock();
}
}
}
}
公平锁:谁先到谁得到锁
非公平锁:大家一块抢
上边是唤醒所有线程,下边是让当前线程睡着。
多线程通信——生产者消费者问题,就像是厨师和服务员一样,当厨师在工作的时候,服务员线程在wait(沉睡),当服务员送餐的时候厨师在(wait)
public class setname {
public static void main(String[] args) {
//多线程通信 生产者与消费者问题
Food f = new Food();
new Cook(f).start();
new Waiter(f).start();
}
//厨师
static class Cook extends Thread{
private Food f;
public Cook(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2==0){
f.setNameAndTaste("老干妈小米粥","香辣味");
}else {
f.setNameAndTaste("煎饼果子","甜辣味");
}
}
}
}
//服务员
static class Waiter extends Thread{
private Food f;
public Waiter(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.get();
}
}
}
//食物
static class Food{
private String name;
private String taste;
//true表示可以生产
boolean flag = true;
public synchronized void setNameAndTaste(String name,String taste){
if(flag){
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
flag = false;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void get(){
if(!flag){
System.out.println("服务员端走的菜的名称是:"+name+",味道是:"+taste);
flag = true;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
线程的状态
线程的状态转换
Java中第三种线程Callable实现方式(作为了解)
返回值会先打印下边的123456789,然后get方法等待子线程执行完成后再执行他自己的线程
判断线程是否执行完
取消线程
线程池是容纳多个线程的容器(相当于一个线程数组)开发中很少使用线程池。(java中有四种线程池)
缓存线程池
向线程池中加入任务(指挥线程池执行新的任务)
再复制那个方法,因为线程池中已经有了缓存,所以会调用在缓存中的
定长线程池
单线程线程池
执行完不会关闭还在等待传入,但是在一定时间后还会自动关闭的
周期线程池
Lambda表达式
以前创建线程的方式
匿名内部类的使用(比原来简便一些)
函数式编程
接口只有一个方法才可以使用lambda表达式
修改后的