Java基础之多线程
多线程创建的三种方式:
-
继承Thread类
-
实现步骤:
- 定义一个类继承Thread;
- 子类重写Thread类的run()方法;
- 启动线程调用start();
-
实现代码:
new Thread(){ @Override public void run() { super.run(); } }.start();
-
实现步骤:
-
实现接口Runnable
-
实现步骤:
- 创建一个类实现Runnable接口
- 重写run();
- 把实现类作为Thread的构造参数传递,创建Thread对象
- 启动线程
-
实现代码:
new Thread(new Runnable() { @Override public void run() { } }).start();
-
实现步骤:
-
实现接口Callable(结合线程池)
-
实现步骤:
-
创建线程池
-
public static ExecutorService newFixedThreadPool(int nThreads)
-
-
实现Callable接口或者Runnable接口
- 重写call()方法或者run()方法
-
调用如下方法即可
-
Future<?> submit(Runnable task)
-
<T> Future<T> submit(Callable<T> task)
-
-
结束任务
-
pool.shutdown()
-
-
创建线程池
-
线程池方式:
`//初始化线程池 ExecutorService pool = Executors.newFixedThreadPool(2); //可以执行Runnable对象或者Callable对象代表的线程 Future<Integer> future = pool.submit(new Callable<Integer>() { private int sum=0; @Override public Integer call() throws Exception { for (int i = 0; i < 100; i++) { sum+=i; } return sum; } }); //获取结果 try { future.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } //结束 pool.shutdown();`
-
使用FutureTask转化
FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() { private int sum=0; @Override public Integer call() throws Exception { for (int i = 0; i < 100; i++) { sum+=i; } return sum; } }); Thread t = new Thread(task); t.start(); try { Integer integer = task.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace();
-
实现步骤:
多线程的调度:
-
线程有两种调度模型:
-
分时调度模型
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片 -
抢占式调度模型
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程 获取的 CPU 时间片相对多一些。
-
分时调度模型
-
线程的优先级:
-
调用方法:
public final void setPriority(int newPriority)
-
优先级参数:
- MAX_PRIORITY
- MIN_PRIORITY
- NORM_PRIORITY
-
注意:
- 线程默认优先级是5。
- 线程优先级的范围是:1-10。
- 线程优先级高仅仅表示线程获取的 CPU时间片的几率高,但是要在次数比较多,或者多次运行的 时候才能看到比较好的效果。
-
调用方法:
-
线程的休眠:
-
调用方法:
public static void sleep(long millis)
-
sleep()
方法不能释放锁,
-
调用方法:
-
线程的插入:
-
调用方法:
public final void join():等待该线程终止
- 在start()方法之后使用.
-
调用方法:
-
线程的守护:
-
调用方法:
public final void setDaemon(boolean on)
:将该线程标记为守护线程或用户线程。 - 当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用
-
调用方法:
-
线程的让步:
-
调用方法:
public static void yield()
:暂停当前正在执行的线程对象,并执行其他线程。 - 只能让线程之间更加和谐,但不能保证线程之间一个执行一次;
-
调用方法:
-
线程的终止:
-
public final void stop()
:让线程停止,过时了,但是还可以使用。 -
public void interrupt()
:中断线程。 把线程的状态终止,并抛出一个InterruptedException。
-
多线程的同步:
-
synchronize同步
- 同步代码块的锁为object;
- 同步方法的锁为this;
- 同步静态方法的锁为该类Class对象;
-
lock同步:
- 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock;
- Lock为一个接口,要使用其实现类ReentrantLock
-
调用的方法:
- lock();
- unlock();
多线程之间的通讯:
- 定义: 不同种类的线程间针对同一资源进行操作。
-
测试案例: 以生产者和消费者多线程体现:
- 资源类:Student
- 设置数据类:SetThread(生产者)
- 获取数据类:GetThread(消费者)
- 测试类:StudentDemo
-
通信最易出现的问题:
- 线程安全问题:线程同步
- 实际意义:等待唤醒机制
-
最优化代码:
public class StudentDemo { public static void main(String[] args) { //创建资源 Student s = new Student(); //设置和获取的类 SetThread st = new SetThread(s); GetThread gt = new GetThread(s); //线程类 Thread t1 = new Thread(st); Thread t2 = new Thread(gt); //启动线程 t1.start(); t2.start(); } } public class Student { private String name; private int age; private boolean flag; // 默认情况是没有数据,如果是true,说明有数据 public synchronized void set(String name, int age) { // 如果有数据,就等待 if (this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 设置数据 this.name = name; this.age = age; // 修改标记 this.flag = true; this.notify(); } public synchronized void get() { // 如果没有数据,就等待 if (!this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 获取数据 System.out.println(this.name + "---" + this.age); // 修改标记 this.flag = false; this.notify(); } } public class GetThread implements Runnable { private Student s; public GetThread(Student s) { this.s = s; } public void run() { while (true) { s.get(); } } } public class SetThread implements Runnable { private Student s; private int x = 0; public SetThread(Student s) { this.s = s; } public void run() { while (true) { if (x % 2 == 0) { s.set("林青霞", 27); } else { s.set("刘意", 30); } x++; } } }