一、线程:
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位
简单理解:应用软件中互相独立,可以同时运行的功能
并发:在同一时刻,有多个指令在单个CPU上交替 执行
并行:在同一时刻,有多个指令在多个CPU上同时执行
二、多线程的实现方式:
①继承Thread类的方式进行实现
代码演示:
package a01threaddemo;
public class MyThread extends Thread{
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"hello world");
}
}
}
测试类:
package a01threaddemo;
public class ThreadDemo1 {
public static void main(String[] args) {
//多线程的第一种启动方式
//1.自己定义一个类继承Thread
//2.重写run方法
//3.创建子类的对象,并启动线程
MyThread t1=new MyThread();
MyThread t2=new MyThread();
t1.setName("线程1");
t2.setName("线程2");
//开启线程用start
t1.start();
t2.start();//两个线程交替执行
}
}
运行结果:
②实现Runnable接口的方式进行实现
代码演示:
package a01threaddemo;
public class MyRun implements Runnable{
@Override
public void run() {
//书写线程要执行的代码
for (int i = 0; i < 100; i++) {
//获取到当前线程的对象
/* Thread t=Thread.currentThread();
System.out.println(t.getName()+"helloWorld");
*/
System.out.println(Thread.currentThread().getName()+"helloWorld");
}
}
}
测试类:
package a01threaddemo;
public class ThreadDemo2 {
public static void main(String[] args) {
//多线程第二种启动方式
//1.自己定义一个类实现Runnable接口
//2.重写里面的run方法
//3.创建自己的类的对象
//4.创建一个Thread类的对象,并开启线程
//创建MyRun的对象
//表示多线程要执行的任务
MyRun mr=new MyRun();
//创建线程对象
Thread t1=new Thread(mr);
Thread t2=new Thread(mr);
//给线程设计名字
t1.setName("线程1");
t2.setName("线程2");
//开启线程
t1.start();
t2.start();
}
}
运行结果:两个线程交替执行
③利用Callable接口和Future接口方式实现
代码演示:
package a01threaddemo;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
//求1~100之间的和
int sum=0;
for (int i = 1; i <= 100; i++) {
sum=sum+i;
}
return sum;
}
}
运行结果:
package a01threaddemo;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class ThreadDemo3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//多线程的第三种方式
//特点:可以获取到多线程运行的结果
//1.创建一个类MyCallable实现Callable接口
//2.重写call(是有返回值的,返回值表示多线程的运行结果)
//3.创建MyCallable的对象(表示多线程要执行的任务)
//4.创建FutureTask的对象(作用:管理多线程运行的结果)
//5.创建Thread的对象(表示线程)
MyCallable mc=new MyCallable();
FutureTask<Integer> ft=new FutureTask<>(mc);
Thread t1=new Thread(ft);
t1.start();
Integer result = ft.get();
System.out.println(result);
}
}
三、常见的成员方法:
1. 前4个代码演示:
package a02threaddemo;
public class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name);
}
public void run(){
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(getName()+"@"+i);
}
}
}
测试类:
package a02threaddemo;
public class ThreadDemo1 {
public static void main(String[] args) throws InterruptedException {
//细节1:如果没有给线程设置名字,线程也是有默认的名字的
//格式:Thread-X(X序号,从0开始的)
//细节2:如果我们要给线程设置名字,可以用set方法进行设置,也可以用构造方法进行设置
//细节3:当JVM虚拟机启动之后,会自动的启动多条线程,其中有一条线程就叫做main线程
//他的作用就是去调用main方法,并执行里面的代码
//在以前,我们写的所有的代码都是运行在main线程中的
//sleep:让线程休眠
//细节1:哪条线程执行到这个方法,哪条线程就会在这里停留对应的时间
//细节2:方法的参数:就表示睡眠的时间,单位毫秒
//细节3:当时间到了之后,线程会自动的醒来,继续执行下面的其他代码
//1.创建线程的对象
MyThread t1=new MyThread("线程1");
MyThread t2=new MyThread("线程2");
//2.开启线程
t1.start();
t2.start();
/* Thread t=Thread.currentThread();
String name = t.getName();
System.out.println(name);
*/
/* System.out.println("1111111111111");
Thread.sleep(5000);
System.out.println("222222222");
*/
}
}
2.线程的优先级:分为抢占式调度和非抢占式调度
Java中是抢占式调度(随机性)
代码演示:
package a02threaddemo;
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
测试类:
package a02threaddemo;
import a01threaddemo.MyRun;
public class ThreadDemo2 {
public static void main(String[] args) {
MyRunnable mr=new MyRunnable();
Thread t1=new Thread(mr,"线程1");
Thread t2=new Thread(mr,"线程2");
// System.out.println(t1.getPriority());//5
// System.out.println(t2.getPriority());//默认优先级是5,最小是1,最大是10
// System.out.println(Thread.currentThread().getPriority());//5
t1.setPriority(1);
t2.setPriority(10);
//优先级越高抢占到CPU的概率越大
t1.start();
t2.start();
}
}
运行结果:
3.守护线程:
细节:当其他的非守护线程执行完毕后,守护线程会陆续结束(并不会执行完所有代码就结束了)
四、 生产者和消费者(常见方法)
void wait()--------->当前线程等待,直到被其他线程唤醒
void notify()-------->随机唤醒单个线程
void notifyAll()-------->唤醒所有线程
等待唤醒机制代码演示
厨师类:
package a03threaddemo;
public class Cook extends Thread {
public void run() {
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
//判断桌子上是否有食物
if (Desk.foodFlag == 1) {
//如果有就等待
try {
Desk.lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
//如果没有就制作食物
System.out.println("厨师做了一碗面条");
//修改桌子上的食物状态
Desk.foodFlag = 1;
//唤醒等待的消费者开吃
Desk.lock.notifyAll();
}
}
}
}
}
}
吃货类:
package a03threaddemo;
public class Foodie extends Thread{
public void run(){
//步骤:
//1.循环
//2同步代码块
//3.判断共享数据是否到了末尾(先写到了末尾因为简单)
//4.没有到末尾情况,执行核心逻辑
while(true){
synchronized (Desk.lock){
if(Desk.count==0){
break;
}else{
//先判断桌子上是否有面条
if(Desk.foodFlag==0) {
//如果没有就等待
try {
Desk.lock.wait();//让当前线程跟锁进行绑定
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else {
//把吃的总数-1
Desk.count--;
//如果有就开吃
System.out.println("吃货正在吃面条,还能再吃" + Desk.count + "碗!!!");
//吃完之后唤醒厨师继续做
Desk.lock.notifyAll();//唤醒绑定这把锁的所有线程
//修改桌子的状态
Desk.foodFlag = 0;
}
}
}
}
}
}
桌子类:
package a03threaddemo;
public class Desk {
//控制生产者和消费者的执行
//foodFlag 0:没有面条 1:有面条
//boolean只能控制两条线程,所以如果以后线程变多了,就不能用了
public static int foodFlag=0;
//总个数:
public static int count=10;
//锁对象
public static Object lock=new Object();
}
测试类:
package a03threaddemo;
public class ThreadDemo2 {
public static void main(String[] args) {
//创建线程对象
Cook c=new Cook();
Foodie f=new Foodie();
//给线程设计名字
c.setName("厨师");
f.setName("吃货");
c.start();
f.start();
}
}
运行结果:
等待唤醒机制(阻塞队列方式实现)
生产者和消费者必须使用同一个阻塞队列
厨师类:
package a04threaddemo;
import java.util.concurrent.ArrayBlockingQueue;
public class Cook extends Thread{
ArrayBlockingQueue<String> queue;
public Cook(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
public void run(){
while(true){
//不断的把面条放在阻塞队列当中
try {
queue.put("面条");
System.out.println("厨师放了一碗面条");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
吃货类:
package a04threaddemo;
import java.util.concurrent.ArrayBlockingQueue;
public class Foodie extends Thread{
ArrayBlockingQueue<String> queue;
public Foodie(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
public void run(){
while(true){
//不断的从阻塞队列中获取面条
//不用写锁,代码底层有锁
try {
String food = queue.take();
System.out.println(food);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
测试类:
package a04threaddemo;
import java.util.concurrent.ArrayBlockingQueue;
public class ThreadDemo {
public static void main(String[] args) {
//1.创建阻塞队列的对象
ArrayBlockingQueue<String> queue=new ArrayBlockingQueue<>(1);
//2.创建线程的对象,并把阻塞队列传递过去
Cook c=new Cook(queue);
Foodie f=new Foodie(queue);
c.start();
f.start();
}
}
运行结果:会死循环