目录
一、线程与进程
二、java的线程使用
一、线程与进程
进程:系统运行的基本单元,当我们启动一个程序,就是一个进程从创建、运行到消亡的过程。进程可能会经历各种不同的状态,一般来说有三种状态。
- 就绪态: 进程已经获得了除cpu以外的所有其它资源,在就绪队列中等待cpu调度
- 执行状态: 已经获得cpu以及所有需要的资源正在运行
- 阻塞状态(等待状态): 进程因等待所需要的资源而放弃处理器,或者进程本来就不拥有处理器,且其它资源也没有满足
状态转换: 就绪态的进程得到cpu调度就会变为执行状态,执行态的进程如果因为休眠或等待某种资源就会变为等待状态,执行态的进程如果时间片到了就会重新变为就绪状态放入就绪队列末尾,等待状态的进程如果得到除cpu以外的资源就会变为就绪状态注意处于等待状态的进程不能直接转变为执行状态,而首先要变为就绪状态,哪怕系统中只有一个进程。
线程:线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。
线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁。
-
新建:就是刚使用new方法,new出来的线程;
-
就绪:就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
-
运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;
-
阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll()方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;
-
销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源;
二、java的线程使用
Thread 是程序中执行的线程。 Java虚拟机允许应用程序同时运行多个执行线程。每个线程都有优先权。 具有较高优先级的线程优先于具有较低优先级的线程执行。 每个线程可能也可能不会被标记为守护进程。 当在某个线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护进程线程。当Java虚拟机启动时,通常会有一个非守护进程线程(通常调用某个指定类的名为main的方法)。 Java虚拟机继续执行线程,直到发生以下任一情况:
- 已调用类Runtime的exit方法,并且安全管理器已允许执行退出操作。
- 通过调用run方法返回或抛出超出run方法传播的异常,所有非守护程序线程的线程都已死亡。
#嵌套类
static class Thread.State #线程状态。
#字段
MAX_PRIORITY #线程可以拥有的最大优先级,1。
MIN_PRIORITY #线程可以拥有的最低优先级,5。
NORM_PRIORITY #分配给线程的默认优先级,10。
#构造方法
Thread() #分配新的 Thread对象。
Thread(Runnable target) #分配新的 Thread对象。
Thread(Runnable target, String name) #分配新的 Thread对象。
Thread(String name) #分配新的 Thread对象。
Thread(ThreadGroup group, Runnable target) #分配新的 Thread对象。
Thread(ThreadGroup group, Runnable target, String name) #分配新的 Thread对象,使其具有 target作为其运行对象,具有指定的 name作为其名称,并且属于 group引用的线程组。
Thread(ThreadGroup group, Runnable target, String name, long stackSize) #分配新的 Thread对象,使其具有 target作为其运行对象,具有指定的 name作为其名称,并且属于 group引用的线程组,并具有指定的 堆栈大小 。
Thread(ThreadGroup group, Runnable target, String name, long stackSize, boolean inheritThreadLocals) #分配新的Thread对象,使其具有target作为其运行对象,具有指定的name作为其名称,属于group引用的线程组,具有指定的stackSize ,并且如果inheritThreadLocals是true ,则继承inheritable thread-local变量的初始值。
Thread(ThreadGroup group, String name) #分配新的 Thread对象。
#主要方法
start() #导致此线程开始执行; Java虚拟机调用此线程的run方法。
yield() #向调度程序提示当前线程是否愿意产生其当前使用的处理器,让出当前cpu调度(不一定成功),不释放锁。
sleep(long millis) #导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性.不会释放锁。
sleep(long millis, int nanos) #导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统定时器和调度程序的精度和准确性。
setName(String name) #将此线程的名称更改为等于参数 name 。
setPriority(int newPriority) #更改此线程的优先级。
setDaemon(boolean on) #将此线程标记为 daemon线程或用户线程。
run() #如果此线程是使用单独的Runnable运行对象构造的,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回。
join() #等待这个线程死亡。
join(long millis) #此线程最多等待 millis毫秒。
join(long millis, int nanos) #此线程最多等待 millis毫秒加上 nanos纳秒。
isAlive() #测试此线程是否存活。
isDaemon() #测试此线程是否为守护程序线程。
isInterrupted() #测试此线程是否已被中断。
interrupt() #中断此线程。不建议使用,推荐线程正常结束
interrupted() #测试当前线程是否已被中断。
holdsLock(Object obj) #当且仅当当前线程在指定对象上保存监视器锁时,返回 true 。
getAllStackTraces() #返回所有活动线程的堆栈跟踪映射。
getContextClassLoader() #返回此线程的上下文 ClassLoader 。
getDefaultUncaughtExceptionHandler() #返回由于未捕获的异常而导致线程突然终止时调用的默认处理程序。
getId() #返回此Thread的标识符。
getName() #返回此线程的名称。
getPriority() #返回此线程的优先级。
getStackTrace() #返回表示此线程的堆栈转储的堆栈跟踪元素数组。
getState() #返回此线程的状态。
getThreadGroup() #返回此线程所属的线程组。
currentThread() #返回对当前正在执行的线程对象的引用。
dumpStack() #将当前线程的堆栈跟踪打印到标准错误流。
enumerate(Thread[] tarray) #将当前线程的线程组及其子组中的每个活动线程复制到指定的数组中。
activeCount() #返回当前线程thread group及其子组中活动线程数的估计值。
checkAccess() #确定当前运行的线程是否具有修改此线程的权限。
2.1通过继承Thread使用
java程序可以有两种方式创建执行线程,一是通过继承该类来构建自己的线程类,Thread类是一个实现Runnable接口的原生类,二是直接实现Runnable接口来构建自己的线程类。
UserThreadOne.java
public class UserThread extends Thread {
private String threadName;
private int index;
private static final int LOOP_NUM = 100;
public UserThread() {
this.threadName = "";
}
public UserThread(String name) {
this.threadName = name;
this.index = 0;
}
@Override
public void run() {
while (index < LOOP_NUM) {
System.out.println(this.threadName + "执行次数:" + this.index++);
}
}
}
ThreadTest.java
public class ThreadTest {
public static void main(String[] args) {
UserThread threadA = new UserThread("线程A");
UserThread threadB = new UserThread("线程B");
threadA.start();
threadB.start();
}
}
执行结果:
2.2通过实现Runnable使用
UserRunnable.java
public class UserRunnable implements Runnable {
private String threadName;
private int index;
private static final int LOOP_NUM = 100;
public UserRunnable() {
this.threadName = "";
}
public UserRunnable(String name) {
this.threadName = name;
this.index = 0;
}
@Override
public void run() {
while (index < LOOP_NUM) {
System.out.println(this.threadName + "执行次数:" + this.index++);
}
}
}
ThreadTest.java
public class ThreadTest {
public static void main(String[] args) {
/*UserThread threadA = new UserThreadOne("线程A");
UserThread threadB = new UserThreadOne("线程B");*/
UserRunnable runnableA= new UserRunnable("线程A");
UserRunnable runnableB = new UserRunnable("线程B");
Thread threadA = new Thread(runnableA);
Thread threadB = new Thread(runnableB);
threadA.start();
threadB.start();
}
}
2.3通过实现Callable使用
Executors 工厂和工具方法Executor , ExecutorService , ScheduledExecutorService , ThreadFactory ,和Callable此包中定义的类。 该类支持以下几种方法:
使用常用配置设置创建和返回ExecutorService设置的方法。
- 创建和返回ScheduledExecutorService设置的方法,使用常用的配置设置。
- 创建并返回“包装”ExecutorService的方法,通过使特定于实现的方法不可访问来禁用重新配置。
- 创建并返回将新创建的线程设置为已知状态的ThreadFactory的方法。
- 创建并返回一个方法Callable出来的其他闭包形式,这样他们就可以在需要的执行方法使用Callable 。
ExecutorService Executor提供管理终止的方法和可生成Future以跟踪一个或多个异步任务进度的方法。 shutdown()方法将允许先前提交的任务在终止之前执行,而shutdownNow()方法可防止等待任务启动并尝试停止当前正在执行的任务。 终止时,执行程序没有正在执行的任务,没有等待执行的任务,也没有任何新任务可以提交。 应关闭未使用的ExecutorService以允许回收其资源。
方法submit延伸的基方法Executor.execute(Runnable)通过创建并返回一个Future ,可用于取消执行和/或等待完成。 方法invokeAny和invokeAll执行最常用的批量执行形式,执行一组任务,然后等待至少一个或全部完成。 ( ExecutorCompletionService类可用于编写这些方法的自定义变体。)
Future 表示异步计算的结果。 提供方法以检查计算是否完成,等待其完成,以及检索计算结果。 只有在计算完成时才能使用方法get检索结果,必要时将其阻塞直到准备就绪。 取消由cancel方法执行。 提供了其他方法来确定任务是否正常完成或被取消。 计算完成后,无法取消计算。 如果您希望使用Future以获取可取消性但未提供可用结果,则可以声明Future表单的Future并返回null作为基础任务的结果。
@Test
public void CallableTest(){
UserThread ut1=new UserThread("线程A");
UserThread ut2=new UserThread("线程B");
UserThread ut3=new UserThread("线程C");
//创建执行服务,三个线程
ExecutorService es = Executors.newFixedThreadPool(3);
try {
//提交线程任务
Future future1 = es.submit(ut1);
//获取线程执行结果
System.out.println(future1.get());
Future future2 = es.submit(ut2);
System.out.println(future2.get());
Future future3 = es.submit(ut3);
System.out.println(future3.get());
}catch (Exception e){
e.printStackTrace();
}finally {
es.shutdown();
}
}
class UserThread implements Callable<String> {
private String threadName;
private int index;
private static final int LOOP_NUM = 10;
public UserThread() {
this.threadName = "";
}
public UserThread(String name) {
this.threadName = name;
this.index = 0;
}
@Override
public String call() throws Exception {
while (index < LOOP_NUM) {
//每次打印休眠0.1毫秒
Thread.sleep(100);
System.out.println(this.threadName + "执行次数:" + this.index++);
}
return this.threadName+" end";
}
}
三、线程的状态
/**
* 测试线程状态
* NEW 尚未启动的线程的线程状态。
* RUNNABLE 可运行线程的线程状态。 处于可运行状态的线程正在Java虚拟机中执行,但它可能正在等待来自操作系统的其他资源,例如处理器。
* BLOCKED 线程的线程状态被阻塞等待监视器锁定。 处于阻塞状态的线程正在等待监视器锁定以在调用Object.wait之后输入同步块/方法或重新输入同步块/方法。。
* WAITING 等待线程的线程状态。 由于调用以下方法之一,线程处于等待状态:
* Object.wait没有超时
* Thread.join没有超时
* LockSupport.park。
* TIMED_WAITING 具有指定等待时间的等待线程的线程状态。 由于在指定的正等待时间内调用以下方法之一,线程处于定时等待状态:
* Thread.sleep
* Object.wait超时
* Thread.join超时
* LockSupport.parkNanos
* LockSupport.parkUntil
* TERMINATED 终止线程的线程状态。 线程已完成执行。
*/
@Test
public void ThreadState() throws InterruptedException {
Stock stock = new Stock();
Thread t = new Thread(() -> {
try {
//休眠0.5毫秒,让主线程检测到阻塞状态
Thread.sleep(500);
//添加20个库存
for (int i = 0; i < 20; i++) {
stock.addStock("2020-10-1-NO." + i);
if (Thread.currentThread().getState() != Thread.State.TERMINATED) {
System.out.println("线程未结束" + Thread.currentThread().getState());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t.getState());
t.start();
//监控t线程的sleep状态
while (t.getState() != Thread.State.TERMINATED) {
Thread.sleep(100);
System.out.println("线程阻塞:" + t.getState());
}
//休眠1毫秒保证在t线程之后结束,获取到stock中的结果
Thread.sleep(1000);
System.out.println("线程结束:" + t.getState());
if (t.getState() == Thread.State.TERMINATED) {
for (int i = 0; i < stock.getNumber(); i++) {
System.out.println(stock.reduceStock());
}
}
}
}
class Stock {
/*数量*/
private int number;
/*物件集合(编码)*/
private LinkedList<String> ls;
public Stock() {
this.ls = new LinkedList<>();
this.number = this.ls.size();
}
/*添加库存*/
public void addStock(String code) {
this.ls.offer(code);
this.number = this.ls.size();
}
/*减少库存*/
public String reduceStock() {
String code = this.ls.poll();
this.number = this.ls.size();
return code;
}
public int getNumber() {
return this.number;
}
四、线程优先级
/**
* priority 越大,优先级越高
* MAX_PRIORITY = 10
* MIN_PRIORITY = 1
* 优先级高的不一定越快得到执行,看cpu的调度
* @throws InterruptedException
*/
@Test
public void ThreadPriority() throws InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println("t1..........");
});
t1.setPriority(2);
t1.start();
Thread t2 = new Thread(() -> {
System.out.println("t2..........");
});
t2.setPriority(Thread.MAX_PRIORITY);
t2.start();
}
五、线程同步
当多个线程对同一资源(对象)进行写操作时(增删改),可能会尝试错误数据,因此要对资源进行同步控制,
错误数据
@Test
public void ThreadSynchronized() throws InterruptedException {
//初始化库存数量
Stock stock = new Stock(10);
new Thread(() -> {
while (stock.getRemain() > 0) {
try {
//休眠以便其他线程能被调用,放大问题出现概率
Thread.sleep(10);
synchronized (stock) {
//减少库存数量
int i = stock.reduceStock();
System.out.println("t1--------" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
while (stock.getRemain() > 0) {
try {
Thread.sleep(10);
synchronized (stock) {
int i = stock.reduceStock();
System.out.println("t1--------" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
while (stock.getRemain() > 0) {
try {
Thread.sleep(10);
synchronized (stock) {
int i = stock.reduceStock();
System.out.println("t3--------" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
//休眠Main线程,以便能观察到其他线程的打印
Thread.sleep(20000);
}
class Stock {
/*数量*/
private int number;
public Stock(int number) {
this.number = number;
}
/*添加库存*/
public void addStock() {
this.number++;
}
/*减少库存*/
public int reduceStock() {
return --this.number;
}
public int getRemain() {
return this.number;
}
}
解决方法
synchronized同步块
...
/**
* synchronized (stock){...} 执行代码块时会对stock对象实例加锁,避免其他线程修改stock实例
* 注意:
* synchronized (obj){...} obj实例必须在代码块中
*
* synchronized type function(...){...} 同步方法锁定this实例。
* 相当于
*
* type function(...){
* synchronized (this){...}
* }
* @throws InterruptedException
*/
new Thread(() -> {
while (stock.getRemain() > 0) {
try {
Thread.sleep(10);
synchronized (stock) {
int i = stock.reduceStock();
System.out.println("t1--------" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
...
synchronized同步方法
/**
* synchronized (stock){...} 执行代码块时会对stock对象实例加锁,避免其他线程修改stock实例
* 注意:
* synchronized (obj){...} obj实例必须在代码块中
*
* synchronized type function(...){...} 同步方法锁定this实例。
* 相当于
*
* type function(...){
* synchronized (this){...}
* }
*/
/*减少库存*/
public synchronized int reduceStock(){
if(this.number>0)
return --this.number;
else return 0;
}
使用原子类定义变量
//直接使用volatile修饰变量不能保证操作原子性,因此对变量修改不能实时反映到内存,其它线程可能读取不到实时的修改后的数据。
class Stock {
/*数量*/
private AtomicInteger number;
public Stock(int number) {
this.number = new AtomicInteger(number);
}
/*添加库存*/
public void addStock() {
//增加一个
this.number.addAndGet(1);
}
/*减少库存*/
public int reduceStock() {
//数目大于0则减少一个
if (this.number.get() > 0) {
return this.number.decrementAndGet();
} else return 0;
}
public int getRemain() {
return this.number.get();
}
}
六、线程通信
/**
* 生产者与消费者:
* 多个线程之间通过.wait()、notify()、notifyAll()来进行通信;通过某个条件如flag=false或size=10等(管程法和信号灯法);来使当前线程等待以及唤醒其他线程;
* 注意点:
* synchronized用于在线程中锁定代码块时,wait()、notify()、notifyAll()方法必须在被锁定的代码块中;否则出现IllegalMonitorStateException异常,不是对象监视器的所有者;
* synchronized用于锁定方法,则wait()、notify()、notifyAll()方法必须在被锁定的方法中
* 线程以三种方式之一成为对象监视器的所有者:
* <p>
* 通过执行该对象的同步实例方法。
* 通过执行在对象上同步的synchronized语句的主体。
* 对于类型为Class,的对象,通过执行该类的同步静态方法。
*
* @throws InterruptedException
*/
@Test
public void ThreadsCommunication() throws InterruptedException {
MyStock stock = new MyStock(20);
new Thread(() -> {
//编号计数
int i = 1;
try {
while (true) {
synchronized (stock) {
//库存是满的就等待
if (stock.isFull()) {
stock.wait();
} else {
//库存不满就添加库存
stock.addStock("SHOPPING-NO." + i++);
//打印当前库存余量
System.out.println(Thread.currentThread().getName() + "库存->" + stock.getRemain());
//唤醒消费者
stock.notifyAll();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "店主").start();
new Thread(() -> {
try {
while (true) {
//制造休眠让另一个消费者有时间消费
Thread.sleep(200);
synchronized (stock) {
//库存是空的就等待添加库存
if (stock.isEmpty()) {
stock.wait();
} else {
//有库存,消费者购买减少一个库存
System.out.println(Thread.currentThread().getName() + "消费->" + stock.reduceStock());
}
//唤醒生产者和另外消费者
stock.notifyAll();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "客户 1").start();
new Thread(() -> {
try {
while (true) {
//制造休眠让另一个消费者有时间消费
Thread.sleep(200);
synchronized (stock) {
//库存是空的就等待添加库存
if (stock.isEmpty()) {
stock.wait();
} else {
//有库存,消费者购买减少一个库存
System.out.println(Thread.currentThread().getName() + "消费->" + stock.reduceStock());
//唤醒生产者和另外消费者
stock.notifyAll();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "客户 2").start();
Thread.sleep(20000);
}
}
class MyStock {
private LinkedList ls;
private int size;
public MyStock(int size) {
this.size = size;
this.ls = new LinkedList();
}
/*添加库存*/
public void addStock(String code) {
//向后添加一个库存商品
this.ls.offer(code);
}
/*减少库存*/
public String reduceStock() {
//获取并删除第一个存入商品
String code = (String) this.ls.poll();
return code;
}
public boolean isFull() {
return this.ls.size() == this.size;
}
public boolean isEmpty() {
return this.ls.size() == 0;
}
public int getSize() {
return size;
}
public int getRemain() {
return this.ls.size();
}