一、多线程概念
线程:
线程是操作系统能够进行运算调度的最小单位。它被包含在进程中,是进程中的实际运作单位
(应用软件中相互独立,可以同时运行的功能)
进程:
进程是程序的基本执行实体
多线程应用场景:
二、并发和并行
并发:
在同一时刻,有多个指令在单个CPU上交替执行
并行:
在同一时刻,有多个指令在多个CPU上同时执行
三、多线程实现方式
继承Thread类的方式:
java">public class Main {public static void main(String[] args) {//多线程 : 继承Thread类实现//定义一个类继承Thread类//重写run方法//创建子类对象 启动线程Method m1=new Method();Method m2=new Method();m1.setName("线程一");m2.setName("线程二");m1.start();m2.start();}
}public class Method extends Thread{@Overridepublic void run() {//线程要执行的代码for(int i=0;i<100;i++){System.out.println(this.getName()+i);}}
}
实现Runnable接口的方式进行实现:
java">public class Main {public static void main(String[] args) {//多线程 : 实现Runnable接口的方式实现//定义一个类实现Runnable接口//重写里面的run方法//创建对象//创建一个Thread类的对象,开启进程Method m1=new Method();Thread thread1=new Thread(m1);Thread thread2=new Thread(m1);thread2.setName("线程二");thread1.setName("线程一");thread1.start();thread2.start();}
}public class Method implements Runnable{public Method() {}@Overridepublic void run() {for (int i=0;i<100;i++){//获取当前线程的对象Thread thread = Thread.currentThread();System.out.println(thread.getName()+i);}}
}
利用Callable接口和Futrue接口方式实现:
java">import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;public class Main {public static void main(String[] args) throws ExecutionException, InterruptedException {//多线程 : 利用Callable接口和Futrue接口方式实现//特点:可以获取多线程运行的结果//定义一个类实现Callable接口//重写里面的call方法 (该方法是有返回值的:返回值表示多线程运行的结果)//创建该类对象 创建FutureTask的对象 (作用是管理多线程运行的结果)//创建一个Thread类的对象,开启进程 (表示线程)Method m1=new Method();FutureTask<Integer> ft=new FutureTask<>(m1); //(作用是管理多线程运行的结果)Thread thread1=new Thread(ft);thread1.start();//获取多线程运行的结果System.out.println(ft.get()); //45}
}import java.util.concurrent.Callable;public class Method implements Callable<Integer> {//泛型<> 根据返回值(线程运行结果)的类型设定public Method() {}@Overridepublic Integer call() throws Exception {int sum=0;for(int i=0;i<10;i++){sum+=i;}return sum;}
}
对比:
四、Thread常见成员方法
前四种:
java">import java.util.concurrent.ExecutionException;public class Main {public static void main(String[] args) throws ExecutionException, InterruptedException {//多线程成员方法//为线程设置名字 方法一:使用setName方法 方法二:子类继承Thread中的构造方法 在构造时直接设置//SetName细节: 线程如果没有设置名字,则会采用默认名字 Thread-X X为序号 从0开始//获取当前线程对象 currentThread()//细节:当JVM虚拟机启动时,会自动启动多条线程// 其中有一条叫做main线程,作用就是去调用main方法并执行里面的代码//sleep(long time)方法 让线程休眠指定的时间//哪条线程执行到这个方法,该线程就会在这里停顿指定的时间//参数表示休眠的时间,单位是毫秒//当休眠时间结束后会醒来,继续执行下面的代码Mythread mt1=new Mythread();Mythread mt2=new Mythread("哈哈");System.out.println(mt1.getName()); //Thread-1System.out.println(mt2.getName()); //哈哈Thread thread = Thread.currentThread();System.out.println(thread.getName()); //mainMythread mythread=new Mythread();mythread.run();}
}public class Mythread extends Thread{@Overridepublic void run() {for(int i=0;i<100;i++){try {//线程休眠 1秒 Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(this.getName() + i);};}public Mythread() {}public Mythread(String name) {super(name);}
}
设置获取线程优先级:
java">import java.util.concurrent.ExecutionException;public class Main {public static void main(String[] args) throws ExecutionException, InterruptedException {Method m=new Method();Thread thread1=new Thread(m,"飞机");Thread thread2=new Thread(m,"大炮");//获取优先级 : 默认值为5System.out.println(thread1.getPriority()); //5//设置线程优先级//优先级≠一定先完成 只是代表概率thread1.setPriority(8);thread2.setPriority(1);thread1.start();thread2.start();}
}public class Method implements Runnable {//泛型<> 根据返回值(线程运行结果)的类型设定public Method() {}@Overridepublic void run() {String name=Thread.currentThread().getName();for(int i=0;i<100;i++){System.out.println(name+i);}}
}
设置守护线程:
java">import java.util.concurrent.ExecutionException;public class Main {public static void main(String[] args) throws ExecutionException, InterruptedException {//设置守护线程 final void setDaemon(boolean on) 设置为守护线程//细节: 当其他的非守护线程执行完毕后,守护线程会陆续结束// 当女神线程执行完毕后,舔狗线程会陆续结束Method m=new Method();Thread thread1=new Thread(m,"女神");Thread thread2=new Thread(m,"舔狗");//设置舔狗为守护进程thread2.setDaemon(true);//女神进程一旦结束,舔狗进程也会慢慢停止thread1.start();thread2.start();}
}public class Method implements Runnable {//泛型<> 根据返回值(线程运行结果)的类型设定public Method() {}@Overridepublic void run() {String name=Thread.currentThread().getName();for(int i=0;i<100;i++){System.out.println(name+i);}}
}
出让线程/礼让线程+ 插入线程/插队线程:
java">import java.util.concurrent.ExecutionException;public class Main {public static void main(String[] args) throws ExecutionException, InterruptedException {//出让线程/礼让线程Mythread m1=new Mythread("fff");Mythread m2=new Mythread("ddd");m1.start();m2.start();//表示把m1 m2这个线程,插入到当前线程之前//当前线程:main线程m1.join();m2.join();for(int i=0;i<10;i++){System.out.println("main线程"+i);}}
}public class Mythread extends Thread{@Overridepublic void run() {for(int i=0;i<100;i++){System.out.println(this.getName() + i);//出让当前CPU使用权 (重新和其他线程抢夺CPU使用权)Thread.yield();};}public Mythread() {}public Mythread(String name) {super(name);}
}
五、线程的生命周期
六、线程的安全问题(同步代码块+同步方法)
java">import java.util.concurrent.ExecutionException;public class Main {public static void main(String[] args) throws ExecutionException, InterruptedException {Method m=new Method();Thread thread1=new Thread(m,"窗口一:");Thread thread2=new Thread(m,"窗口二:");Thread thread3=new Thread(m,"窗口三:");thread1.start();thread2.start();thread3.start();}
}public class Method implements Runnable {//泛型<> 根据返回值(线程运行结果)的类型设定private static int ticket=0;//锁对象一定要是唯一的 (使用static修饰)private static Object obj=new Object();public Method() {}@Overridepublic void run() {String name=Thread.currentThread().getName();//同步代码块synchronized (obj){while (true) {if (ticket < 100) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket++;System.out.println(name + ticket);} elsebreak;}}}
}public class Method implements Runnable {//泛型<> 根据返回值(线程运行结果)的类型设定private static int ticket=0;//锁对象一定要是唯一的 (使用static修饰)private static Object obj=new Object();public Method() {}@Overridepublic void run() {//同步方法extracted();}private synchronized static void extracted() {synchronized (obj){String name=Thread.currentThread().getName();while (true) {if (ticket < 100) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket++;System.out.println(name + ticket);} elsebreak;}}}}
七、Lock锁
java">import java.util.concurrent.ExecutionException;public class Main {public static void main(String[] args) throws ExecutionException, InterruptedException {Method m=new Method();Thread thread1=new Thread(m,"窗口一:");Thread thread2=new Thread(m,"窗口二:");Thread thread3=new Thread(m,"窗口三:");thread1.start();thread2.start();thread3.start();}
}import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Method implements Runnable {//泛型<> 根据返回值(线程运行结果)的类型设定private static int ticket=0;//锁对象一定要是唯一的 (使用static修饰)//Lock是一个接口,需要用其实现类创建对象private static Lock lock=new ReentrantLock();public Method() {}@Overridepublic void run() {//同步代码块String name=Thread.currentThread().getName();while (true) {lock.lock();try {if (ticket < 100) {Thread.sleep(100);ticket++;System.out.println(name + ticket);} else {break;}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {lock.unlock();}}}
}
八、等待唤醒机制
java">import java.util.concurrent.ExecutionException;public class Main {public static void main(String[] args) throws ExecutionException, InterruptedException {Cook c=new Cook();Foodeat f=new Foodeat();c.setName("厨师");f.setName("吃货");c.start();f.start();}
}public class Foodeat extends Thread{@Overridepublic void run() {/*1、循环2、同步代码块3、判断共享数据是否到了末尾(到了末尾)4、判断共享数据是否到了末尾(没有到达末尾,执行核心逻辑)*/while(true){synchronized (Table.lock){if(Table.count==0){//到了末尾break;}else{//1、判断是否有食物//2、如果有则开吃 修改食物总数 唤醒厨师继续做//3、如果没有则等待//4、修改桌子状态if(Table.foodflag==0){//桌子上没有食物 进行等待try {Table.lock.wait(); //让当前线程和锁进行绑定} catch (InterruptedException e) {throw new RuntimeException(e);}}else{//桌子上有食物 开吃Table.count--;System.out.println("吃货正在吃面,还能再吃"+Table.count+"碗面");Table.foodflag=0;Table.lock.notifyAll(); //唤醒厨师去做菜}}}}}}public class Cook extends Thread {@Overridepublic void run() {while(true){synchronized (Table.lock){//判断共享数据是否到达末尾if(Table.count==0){//到达末尾:已经吃不下了break;}else{//没有,对是否有食物进行判断if(Table.foodflag==0){//没有食物System.out.println("厨师已经做了一碗面");Table.foodflag=1;//唤醒吃货开吃Table.lock.notifyAll();}else{//有食物try {Table.lock.wait(); //等待 将该线程与lock锁绑定,方便后续唤醒} catch (InterruptedException e) {throw new RuntimeException(e);}}}}}}
}import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Table {public static int count=10;//foodflag=1: 桌子上有食物 foodflag=0 :桌子上没有食物public static int foodflag=0;//锁对象public static Lock lock=new ReentrantLock();
}
九、阻塞队列实现等待唤醒机制
java">import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutionException;public class Main {public static void main(String[] args) throws ExecutionException, InterruptedException {//利用阻塞队列完成等待唤醒机制//生产者和消费者必须使用同一个队列//1、创建阻塞队列对象ArrayBlockingQueue<String> queue=new ArrayBlockingQueue<>(1); //指定1为队列上限Cook c=new Cook(queue);Foodeat f=new Foodeat(queue);c.setName("厨师");f.setName("吃货");c.start();f.start();}
}import java.util.concurrent.ArrayBlockingQueue;public class Foodeat extends Thread{ArrayBlockingQueue<String> queue;public Foodeat(ArrayBlockingQueue<String> queue){this.queue=queue;}@Overridepublic void run() {while(true){try {String food=queue.take();System.out.println("吃货吃了一碗"+food);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}import java.util.concurrent.ArrayBlockingQueue;public class Cook extends Thread {ArrayBlockingQueue<String> queue;public Cook(ArrayBlockingQueue<String> queue){this.queue=queue;}@Overridepublic void run() {while(true){try {queue.put("面条");System.out.println("厨师做了一碗面");} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
十、多线程的六种状态