Thread

线程概述

 

 

 

 

 

线程与进程的区别

进程对应的是一个服务,线程是进程中的执行单元

一个进程可以包含多个线程,一个线程一定属于某个进程

 

多线程特点:线程是异步的,线程是并发的,多个线程同时工作,线程与线程之间是互不干扰的

 

例子:火车站(进程)和售票窗口(线程)火车站 的关系

 

关于线程的创建方式

三种方法

继承Thread类

方法一:继承Thread类,重写run()方法

设置线程的名称

获取当前正在运行的线程

 

实现Runnable接口

方法二:实现一个Runnable接口

此方法的实现类只是一个可运行类,不能被称为线程类

 

 

通过匿名内部类来创建线程对象

 

线程的生命周期

当一个线程调用了start()方法之后,该线程处于就绪状态,处于就绪状态的线程拥有争夺CPU资源的使用权力

当一个线程争夺到了CPU使用权之后,就回去执行线程,运行run方法,这个时候的线程处于运行状态

当线程在运行的过程中出现了控制台打印、睡眠sleep等需要等待的操作,这个时候线程会处于阻塞状态,处于阻塞状态的线程,会释放CPU使用权

当一个线程执行完run方法中所有的代码就会跳出run方法,这个时候线程处于死亡状态 isAlive()方法可以判断线程是否存活

 

 

Run方法和start方法的区别

Run是执行方法,start是启动方法

start启动不一定执行run方法

 

xxxxxxxxxx100 1package com.os467;2​3import com.os467.Annotion.Table;4​5import java.lang.annotation.Annotation;6import java.lang.reflect.Field;7​8​9public class ORM {10​11    private ORM(){12​13   }14​15    public static String getSql(Object obj){16​17        StringBuilder stringBuilder = new StringBuilder();18​19        //获取obj字节码对象20        Class aClass = obj.getClass();21​22        Table annotation = (Table)aClass.getAnnotation(Table.class);23​24        //表的名称25        String table = null;26​27        if (annotation != null){28​29            //获取表的名称30            table = annotation.value();31​32            stringBuilder.append("select * from "+ table +" where ");33​34       }35​36        //获取属性字节码对象37        Field[] declaredFields = aClass.getDeclaredFields();38​39        for (Field declaredField : declaredFields) {40​41            //打破封装42            declaredField.setAccessible(true);43​44            com.os467.Annotion.Field annotation1 = declaredField.getAnnotation(com.os467.Annotion.Field.class);45​46            //注解不为空的情况下拼接47            if (annotation1 != null){48​49                try {50​51                    //获取属性值52                    Object o = declaredField.get(obj);53​54                    //检查email属性是否有多个","隔开55                    if (o != null && o.toString().contains(",")){56​57                        stringBuilder.append(annotation1.value() + " in(" + o + ") and");58​59                   }else if (o != null && !o.toString().equals("0")){60​61                        stringBuilder.append(annotation1.value() + " = ");62​63                        if (o instanceof String){64​65                            stringBuilder.append("'" + o + "'" + " and ");66​67                       }68​69                        if (o instanceof Integer){70​71                            stringBuilder.append(o + " and ");72​73                       }74​75                   }76​77​78               } catch (IllegalAccessException e) {79                    e.printStackTrace();80               }81​82           }83​84       }85​86        String s = stringBuilder.toString();87​88        //替换掉最后的and 89        s = s.substring(0,s.length() - 4);90​91        if (s.contains("=")){92​93            return s;94​95       }96​97        return null;98   }99​100}java

哪个线程的优先级比较高,抢到的CPU时间片的概率就多一些,java采用的就是抢占式调度模型

 

平均分配CPU时间片,每个线程占用的CPU时间片时间长度一样,平均分配,一切平等,有一些编程语言,线程调度模型采用 的是这种方式

 

使用哪种方式创建线程比较好?

实现runnable接口,因为我们以后是面向接口开发

因为如果继承了Thread类,那么类的可扩展性就降低了

以后我们在写程序,尽量使用Runnable接口方式来创建线程对象,面向接口开发,程序的可扩展性会提高

 

问题:以下代码一共开启了几个线程?

答案:一共开启了4个线程:

两个普通线程 调用了start()方法

守护线程(后台线程):垃圾回收线程

主线程(main线程):JVM调用主函数

 

线程在内存上的分布,以及执行流程

 

在主线程调用了start方法之后,会开辟分支栈,每个栈都会去执行不同的任务,栈与栈之间的资源是不共享的

 

堆的资源是共享的,因为JVM上只有一个堆,只要你去创建引用数据类型的实例,都是在堆中开辟空间

 

方法区中的资源也是共享的,因为内存中只能有一个方法区

 

获取、设置线程优先级方法

 

yield()让位方法

暂停当前正在执行的线程对象,并执行其他线程

yield()方法的执行会让当前线程从"运行状态"回到"就绪状态"

Thread.yield();

 

多线程其它方法

join()方法

join()当前线程进入阻塞,指定线程执行,直到指定线程结束当前线程才可以继续

线程a{

线程b.join()

}

将线程b合并入线程a,线程a等待线程b结束后才继续运行

注意:线程合并必须要有两个不同的线程

 


 

suspend()线程自己把自己挂起

resume线程自己把自己唤醒

 

龟兔赛跑案例

兔子线程类

乌龟线程类

测试类


 

关闭线程方法

//tl.stop()中断线程

//interrupt中断睡眠

//tl.isAlive()判断指定线程是否处于活动状态

 

守护线程

用户线程

守护线程(后台线程)

 

一般守护线程是一个死循环,所有的用户线程只要结束,守护线程自动结束

守护线程一般指的是后台线程,守护线程一般会去守护一个用户线程,用户线程启动,守护线程随之启动,用户线程结束,守护线程也结束,例如垃圾回收线程

设置守护线程方法:普通线程对象.setDaemon(true)

 

定时器

在java的类库中已经写好了一个定时器:java.util.Timer

 

实现线程的第三种方式(Callable结合Task)

FutureTask futureTask = new FutureTask(new Callable())

缺点:由于需要获取返回值,此方法会造成原线程的阻塞

 

线程安全

线程存在不安全问题条件

 

synchronized线程同步锁

堆内存和方法区中的内存是数据共享的

栈内存中的数据是不共享的

synchronized(线程共享对象)

虽然常量也能作为线程共享对象,但是会约束到所有执行这块代码的线程,会降低没有线程安全问题的线程的运行效率,我们只保证具有线程安全问题的线程受到约束即可,即提供需要受到约束的线程的共享对象

 

修饰实例方法,锁住的对象就是this(调用此方法的实例),锁住的内容就是方法内容

public synchronized void method(){}

 

修饰静态方法,拥有的锁就是类锁,只要传入的对象是属于这个类的,就会使得线程同步(与给同步代码块提供类的字节码对象作为共享对象效果相同)

 

线程同步之后,每个线程在进入同步代码块之前就会发生等待

synchronized同步代码块

 

线程同步

线程A和线程B,各自执行各自的,A不管B,B不管A,谁也不需要等谁,这种编程模型叫做:异步编程模型,其实就是:多线程并发,效率较高

线程A和线程B,在线程A执行的时候,必须等待B线程执行结束,或者说在A线程执行的时候,必须等待B线程执行结束,两个线程之间发生了等待关系,这就是同步程模型,效率较低

 

锁池和等待池

 

假设线程A调用了某个对象的wait方法,线程A就会释放该对象的锁,同时线程A就进入到了该对象的等待池中,进入等待池中的线程不会去竞争该对象的锁

 

Object类中的wait和notify方法

 

此对象其实就是多线程中的共享对象

使用此方法必须保证当前多线程处于线程同步状态

当共享对象是类的实例的时候,可以直接在类的方法中写waitnotify方法

 

notify()方法作用

只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会

 

notifyAll()方法的作用

会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会

 

需求:生产者和消费者关系

仓库

仓库里面存放的就是生产者生产出来的商品,需要交给消费者去消费

要保证生产者与消费者均衡的状态:

如果仓库中没有产品了,这个时候消费者线程就不能再进行消费了,需要交给生产者线程去生产,如果仓库已经满了,仓库已经有产品的情况下,生产者线程就不用再生产了,要交由消费者线程去消费

 

生产者与消费者wait、notify

要想使用wait和notify方法,必须是在线程同步的前提下

 

Consumer

 

House

 

Producer

 

WaitDemo

 

volatile关键字

可以修饰属性,实现了多线程中的可见性,让线程之间及时的通知此属性的数据更新