并发
研究并发问题的最强理由:如果视而不见,你就会遭其反噬
- 并发编程是程序分解成多个分离的,独立运行的子任务,每个子任务由执行线程驱动
- 线程就是进程中一个独立的顺序控制流,因此,单个进程也可以拥有多个并发执行的任务
- CPU轮流给每个任务分配时间,让每个任务都觉得自己一直在占有CPU
定义任务
线程可以驱动任务
定义任务:实现Runnable接口并编写run方法
Thread类
把Runnable对象交给一个Thread构造器
编写一个类实现Runnable接口的run方法,run方法是一个循环
Thread.yield:告诉线程调度器这个线程的主要部分已经执行完了,可以切换到其他的线程中去了
线程调度器:如果你的电脑有多个处理器,线程处理器会在这些处理器之间默默的分发线程
package com.company;
/**
* @ClassName LiftOff
* @Description
* @Auther liuxiansen
* @Date 2020/4/6 2:59 下午
**/
public class LiftOff implements Runnable {
private int num = 10;
private static int IDCOUNT = 0;
private final int ID = IDCOUNT++;
public LiftOff(){
}
@Override
public void run() {
while (num > 0){
System.out.print("#(" + ID + ")time: " + num-- + "!!! ");
Thread.yield();
}
System.out.println();
}
}
建一个新的Thread对象并新建一个LiftOff对象给他的构造器
start:为该线程的执行初始化,然后调用run方法
10个LiftOff对象在并行的执行,CPU轮流执行每个线程
package com.company;
/**
* @ClassName Test
* @Description
* @Auther liuxiansen
* @Date 2020/3/29 8:55 上午
**/
public class Test{
public static void main(String [] args) {
for (int i=0;i<10;i++){
Thread a = new Thread(new LiftOff());
a.start();
}
System.out.println("start!!!");
}
}
Executor
javaSE5中的java.util.concurrent包中的Executor(执行器)可以帮助你管理Thread对象
ExecutorService:具有生命周期的Executor
CachedThreadPool:为每个任务创建一个线程
使用Executor管理Thread
shutdown:防止新任务提交给当前的Executor,Executor将运行在shutdown之前提交的所有任务
public static void main(String [] args) {
ExecutorService service = Executors.newCachedThreadPool();
for(int i=0;i<10;i++){
service.execute(new LiftOff());
}
service.shutdown();
}
FixedThreadPool:使用有线的线程执行任务
传入初始化的线程参数,使用1个线程完成和使用10个线程完成所有的任务结果是不一样的
FixedThreadPool能创建的Thread对象最多就是参数个
public static void main(String [] args) {
ExecutorService service = Executors.newFixedThreadPool(5);
for(int i=0;i<10;i++){
service.execute(new LiftOff());
}
service.shutdown();
}
在任何线程池中,现有线程在可能情况下会被自动复用
CachedThreadPool通常会创建于所需要的线程数相等的线程
通常情况下使用CachedThreadPool,当出现问题的时候使用FixedThreadPool
SingleThreadExecutor:线程数量为1的FixedThreadPool,处理多任务时任务排队,只有当前一个任务完成时,才开始下一个任务
使用SingleThreadExecutor
每个线程都是等前一个线程完成后才创建执行
public static void main(String [] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
for(int i=0;i<10;i++){
service.execute(new LiftOff());
}
service.shutdown();
}
###从任务中产生返回值
Runnable是执行工作的独立任务,不能返回任何值
如果线程需要返回值,需要实现Callable接口,重写其中的call方法
Callable是泛型的
package com.company;
import java.util.concurrent.Callable;
/**
* @ClassName LiftOff
* @Description
* @Auther liuxiansen
* @Date 2020/4/6 2:59 下午
**/
public class LiftOff implements Callable<String> {
private int num = 10;
private static int IDCOUNT = 0;
private final int ID = IDCOUNT++;
public LiftOff(){
}
@Override
public String call() throws Exception {
return "#(" + ID + ")run! ";
}
}
使用ExecutorServer的submit方法调用线程,返回Future对象,调用Future的get方法返回线程返回的信息
package com.company;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @ClassName Test
* @Description
* @Auther liuxiansen
* @Date 2020/3/29 8:55 上午
**/
public class Test{
public static void main(String [] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
List<Future<String>> list = new ArrayList<>();
for(int i=0;i<10;i++){
list.add(executorService.submit(new LiftOff()));
}
try {
for(Future<String> future : list){
System.out.println(future.get());
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
}
休眠
使用sleep让线程阻塞一段时间
package com.company;
/**
* @ClassName LiftOff
* @Description
* @Auther liuxiansen
* @Date 2020/4/6 2:59 下午
**/
public class LiftOff implements Runnable {
private int num = 10;
private static int IDCOUNT = 0;
private final int ID = IDCOUNT++;
public LiftOff(){
}
@Override
public void run() {
while (num > 0){
System.out.print("#(" + ID + ")time: " + num-- + "!!! ");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println();
}
}
想要控制任务执行顺序:
- 使用同步控制
- 不使用线程,编写自己的协作例程
优先级
调度器会让优先级高的先执行,但并不意味着让优先级低的不执行(造成死锁),优先级低的仅仅是执行频率较低
绝大多数时间里,线程应该按照默认的优先级执行,改变优先级通常是错误的
Thread.currentThread:获取驱动当前的Thread对象的引用
package com.company;
/**
* @ClassName LiftOff
* @Description
* @Auther liuxiansen
* @Date 2020/4/6 2:59 下午
**/
public class LiftOff implements Runnable {
private int num = 10;
private static int IDCOUNT = 0;
private final int ID = IDCOUNT++;
private int priority;
public LiftOff(int priority){
this.priority = priority;
}
@Override
public void run() {
Thread.currentThread().setPriority(priority);
while (num > 0){
System.out.println("#(" + ID + ")time: " + num-- + "!");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println();
}
}
让步
Thread.yield:暗示调度器这个线程的主要工作已经做完,可以去做别的线程了,仅仅是暗示(调度器不一定会采纳)
在任何重要的调度工作中不能依赖于 yield
后台程序
后台程序:在程序运行时在后台为程序提供服务的线程,并且这种线程不属于程序不可或缺的一部分
程序的所有非后台程序终止时所有的后台程序也会被杀死,但是只要有一个非后台程序在运行,后台程序就不会被杀死
在Thread调用start之前setDaemon
如果主程序的执行时间短,后台线程没有执行完就会被杀死
public static void main(String [] args) {
for (int i=0;i<10;i++){
Thread thread = new Thread(new LiftOff(Thread.MAX_PRIORITY));
thread.setDaemon(true);
thread.start();
}
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
可以使用isDaemon方法判断一个线程是否是后台线程
后台线程在不执行finally子句的情况下是不会终止run方法的
package com.company;
import java.util.concurrent.TimeUnit;
/**
* @ClassName LiftOff
* @Description
* @Auther liuxiansen
* @Date 2020/4/6 2:59 下午
**/
public class LiftOff implements Runnable {
@Override
public void run() {
try {
System.out.println("new thread starting!");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("thread stop");
}
}
}
package com.company;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @ClassName Test
* @Description
* @Auther liuxiansen
* @Date 2020/3/29 8:55 上午
**/
public class Test{
public static void main(String [] args) {
Thread thread = new Thread(new LiftOff());
//thread.setDaemon(true);
thread.start();
}
}
当线程为后台线程时,finally不会执行
注释掉setDaemon后,finally就会执行
程序执行完成后不会给后台线程一个体面的结束
编码的变体
直接继承Thread类
package com.company;
import java.util.concurrent.TimeUnit;
/**
* @ClassName LiftOff
* @Description
* @Auther liuxiansen
* @Date 2020/4/6 2:59 下午
**/
public class LiftOff extends Thread {
@Override
public void run() {
try {
System.out.println("new thread starting!");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("thread stop");
}
}
}
package com.company;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @ClassName Test
* @Description
* @Auther liuxiansen
* @Date 2020/3/29 8:55 上午
**/
public class Test{
public static void main(String [] args) {
new LiftOff().start();
}
}
自管理
package com.company;
import java.util.concurrent.TimeUnit;
/**
* @ClassName LiftOff
* @Description
* @Auther liuxiansen
* @Date 2020/4/6 2:59 下午
**/
public class LiftOff implements Runnable {
private Thread thread = new Thread(this);
public LiftOff(){
thread.start();
}
@Override
public void run() {
try {
System.out.println("new thread starting!");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("thread stop");
}
}
}
package com.company;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @ClassName Test
* @Description
* @Auther liuxiansen
* @Date 2020/3/29 8:55 上午
**/
public class Test{
public static void main(String [] args) {
new LiftOff();
}
}
术语
对线程Thread没有任何的实际的控制权
线程只是执行赋予他的任务
错误的理解:线程是一个任务
java的线程机方式来源于c的p线程方式(应深入研究)
任务:要执行的工作
线程:驱动任务的机制
加入一个线程
package com.company;
import java.util.concurrent.TimeUnit;
/**
* @ClassName LiftOff
* @Description
* @Auther liuxiansen
* @Date 2020/4/6 2:59 下午
**/
public class LiftOff extends Thread {
public LiftOff(String name){
super(name);
this.start();
}
@Override
public void run() {
try {
System.out.println("Thread:" + Thread.currentThread().getName() + " starting");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + " stoped");
}
}
}
package com.company;
/**
* @Author redarm
* @Date 2020/4/9 下午7:16
**/
public class RightNow extends Thread {
private LiftOff liftOff;
public RightNow(String name, LiftOff liftOff){
super(name);
this.liftOff = liftOff;
this.start();
}
@Override
public void run(){
try {
liftOff.join();
System.out.println(Thread.currentThread().getName() + " started");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + " stoped");
}
}
}
package com.company;
import java.io.*;
import java.nio.channels.FileChannel;
/**
* @ClassName Test
* @Description
* @Auther liuxiansen
* @Date 2020/3/29 8:55 上午
**/
public class Test{
public static void main(String [] args) throws IOException {
LiftOff liftOff1 = new LiftOff("liftoff1");
RightNow rightNow1 = new RightNow("rightnow1",liftOff1);
}
}
rightnow 中liftoff.join,所以只有liftoff线程执行完后rightnow才开始执行
共享受限资源
防止两个任务访问相同的资源:
在资源被一个任务使用的时候加上锁
当任务访问synchronized的资源的时候:
- 检查锁是否可用
- 获取锁
- 执行代码
- 释放锁
在对象的方法上加上关键字synchronized:
- 这个方法只能同时被一个任务调用
- 一个对象的多个synchronized方法只能同时被调用一个
- 一个synchronized方法被调用的时候,调用其他的synchronized方法的任务就会等待
- 应该把成员声明为private才有意义,才能控制域的访问
每个访问临界共享资源的方法都必须被同步
package com.company;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* @Author redarm
* @Date 2020/4/9 下午8:23
**/
public class Stone extends Thread {
private String storyOfStone;
public Stone(String name){
super(name);
this.read();
this.start();
}
@Override
public void run(){
this.read();
}
private synchronized void read(){
try {
System.out.println(Thread.currentThread().getName() + " started");
BufferedReader bufferedReader = new BufferedReader(new FileReader("test_file"));
String ss;
StringBuilder sb = new StringBuilder();
Thread.sleep(1000);
while ((ss = bufferedReader.readLine()) != null){
sb.append(ss + "\n");
}
this.storyOfStone = sb.toString();
bufferedReader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + " stoped");
}
}
}
使用显示的Lock对象
Lock对象必须显示的创建,锁定和释放
使用Lock的ReentrantLock实现,
- lock:锁住
- unlock:解锁
package com.company;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author redarm
* @Date 2020/4/9 下午8:23
**/
public class Stone extends Thread {
private String storyOfStone;
private Lock lock = new ReentrantLock();
public Stone(String name){
super(name);
this.read();
this.start();
}
@Override
public void run(){
this.read();
}
private void read(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + " started");
BufferedReader bufferedReader = new BufferedReader(new FileReader("test_file"));
String ss;
StringBuilder sb = new StringBuilder();
Thread.sleep(1000);
while ((ss = bufferedReader.readLine()) != null){
sb.append(ss + "\n");
}
this.storyOfStone = sb.toString();
bufferedReader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + " stoped");
lock.unlock();
}
}
}
使用tryLock尝试获取锁,如果其他人已经获取了这个锁,那么就去执行其他的事情
lock.tryLock(2, TimeUnit.SECONDS);