java多线程(Thread)
并发与并行
并发:在同一时刻,有多个指令在单个cpu上交替执行
并行:在同一时刻,有多个指令在多个cpu上同时运行
多线程的实现方式
1.继承Thread类的方式进行实现
2.实现Runnable接口的方式进行实现
3利用Callable接口和Future接口方式实现
方式一:继承Thread类
1.自己定义一个类继承Thread
2.重写run方法
public class MyThread extends Thread{ @Override public void run() { //书写线程要执行的代码 for(int i=0;i<100;i++){ System.out.println(getName()+"bobo"); } } }
3.创建子类的对象,并启动线程
public class Test01 { public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); t1.setName("线程1"); t2.setName("线程2"); t1.start(); t2.start(); } }
方式二:实现Runnable接口
1.自己定义一个类实现Runnable接口
2.重写里面的run方法
public class MyThread2 implements Runnable{ @Override public void run() { //书写线程要执行的代码 //获取到当前线程的对象 Thread t=Thread.currentThread(); for(int i=0;i<100;i++){ System.out.println(t.getName()+"bobo"); } } }
3.创建自己的类的对象
4.创建一个Thread类的对象,并开启线程
public class Test01 { public static void main(String[] args) { //表示多线程要执行的任务 MyThread2 myThread2 = new MyThread2(); //创建线程对象 Thread t1 = new Thread(myThread2); Thread t2 = new Thread(myThread2); //给线程设置名字 t1.setName("线程1"); t2.setName("线程2"); //开启线程 t1.start(); t2.start(); } }
方式三:利用Callable接口和Future接口
特点:可以获取到多线程运行的结果
1.创建一个自己类来实现Callable接口
2.重写call(是有返回值的,表示多线程运行的结果)
import java.util.concurrent.Callable; public class MyThread3 implements Callable<Integer> { @Override public Integer call() throws Exception { //求1-100之间的和 int sum=0; for(int i=1;i<=100;i++){ sum=+sum; } return sum; } }
3.创建自己类的对象(表示多线程要执行的任务)
4.创建FutureTask的对象(作用管理多线程运行的结果)
5.创建Threda类的对象,并启动(表示线程)
import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class Test01 { public static void main(String[] args) throws ExecutionException, InterruptedException { //表示多线程要执行的任务 MyThread3 myThread3 = new MyThread3(); //创建FutureTask对象(作用管理多线程运行的结果) FutureTask<Integer> ft = new FutureTask<>(myThread3); //创建线程的对象 Thread t1 = new Thread(ft); //启动线程 t1.start(); //获取多线程运行的结果 Integer rs = ft.get(); System.out.println(rs); } }
多线程三种实现方式的对比
优点 | 缺点 | |
---|---|---|
继承Thread类 | 编程比较简单,可以直接使用Thread类中的方法 | 可以扩展性较差,不能再继承其他类 |
实现Runnable接口 | 扩展性强实现该接口的同时还可以继承其他的类 | 编程相对复杂,不能直接使用Thread类中的方法 |
实现Callable接口 | 扩展性强实现该接口的同时还可以继承其他的类 | 编程相对复杂,不能直接使用Thread类中的方法 |
常见的成员方法
方法名称 | 说明 |
---|---|
String getName() | 返回此线程的名称 |
void setName(String name) | 设置线程的名称(构造方法也可以设置名称) |
static Thread currentThread | 获取当前线程的对象 |
static void sleep(long time) | 让线程休眠指定的时间,单位为毫秒 |
setPriority(int newPriority) | 设置线程的优先级 |
final int getPriority() | 获取线程的优先级 |
final void setDaemon(boolean on) | 设置为守护线程 |
public static void yield() | 出让线程/礼让线程 |
public static void join() | 插入线程/插队线程 |
线程的生命周期
线程的安全的问题
同步代码块
把操作共享数据的代码锁起来
格式:
synchronized(锁){
操作共享数据的代码
}
特点1:锁默认打开,有一个线程进去了,锁自动关闭
特点2:里面的代码全部执行完毕,线程出来,锁自动打开
锁对象一般用当前类的字节码文件对象,例如上图使用MyThread.class
同步方法
就是把synchronized关键字加到方法上
格式:
修饰符 synchronized 返回值类型 方法名 (方法参数){……}
特点1:同步方法是锁住方法里面所有的代码
特点2:锁对象不能自己指定(非静态:this,静态:当前类的字节码文件对象)
死锁
避免死锁错误
线程A和线程B,线程A的a锁和线程B的b锁,a锁和b锁相互等待形成死锁
不要嵌套锁
生产者与消费者(等待唤醒机制)
生产者消费者模式是一个十分经典的多线程协作的模式
两个线程,生产者(生产数据),消费者(消费数据)
情况1:(消费者等待)
情况2:(生产者等待)
常见方法
方法名称 | 说明 |
---|---|
void wait() | 当前线程等待,直到被其他线程唤醒 |
void notify() | 随机唤醒单个线程 |
void notifyAll() | 唤醒所有线程 |
缓存区代码块
public class Cache { //缓存区 //控制生产者和消费者的操作 //缓存数据0:表示有没有数据,1表示有数据 public static int state=0; //总数据大小 public static int number=10; //创建锁对象 public static Object lock=new Object(); }
生产者代码块
public class Producer extends Thread{ @Override public void run() { while (true){ synchronized (Cache.lock){ //总数据为0时停止 if(Cache.number==0){ break; } else { //判断是否有数据 if (Cache.state==1) { //有数据,就等待 try { Cache.lock.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } else{ //如果没有数据就生产数据 System.out.println("生产者生产数据"); //修改状态 Cache.state=1; //唤醒消费者消费 Cache.lock.notifyAll(); } } } } } }
消费者代码块
public class Consumer extends Thread{ @Override public void run() { while (true){ synchronized (Cache.lock){ //总数据为0时停止 if(Cache.number==0){ break; } else { //判断缓冲区是否有数据 if(Cache.state==0){ //没有就等待 try { //当前线程与锁进行绑定 Cache.lock.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } else { //有数据就消费 Cache.number--; System.out.println("消费者消费数据,还能消费"+Cache.number+"次"); //唤醒生产者,继续生产 Cache.lock.notifyAll(); //修改状态 Cache.state=0; } } } } } }