多线程
三种创建方式
一丶继承Thread类(重点)
二丶实现Runnable接口(重点)
三丶实现Callable接口(了解)
Thread
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
-
创建线程对象,调用start(方法启动线程
public class TestThread1 extends Thread {
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println(“我在看代码”+i);
}
}public static void main(String[] args) { //创建一个线程对象 TestThread1 testThread1 = new TestThread1(); //调用start()方法开启线程 testThread1.start(); //main线程,主线程 for (int i = 0; i < 200; i++) { System.out.println("我在学习多线程"+i); } }
}
-
总结:线程开启不一定执行,有CPU调度执行
实现Runnable
-
定义MyRunnable类实现Runnable接口
-
实现run()方法,编写线程执行体
-
创建线程对象,调用start()方法启动线程
public class TestThread3 implements Runnable {
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println(“我在看代码” + i);
}
}public static void main(String[] args) { //创建实现类对象 TestThread3 testThread3 = new TestThread3(); //创建线程对象,通过线程对象来开启我们的线程,代理 new Thread(testThread3).start(); for (int i = 0; i < 200; i++) { System.out.println("我在学习多线程" + i); } } }
总结:
继承Thead类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免OOP单继承局限性
实现Runnable接口
- 实现接口Runnable具有多线程能力
- 启动线程:传入目标对象+Thread对象.start()
- 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
多个线程操作同一个对象
//多个线程同时操作一个对象
public class TestThread4 implements Runnable {
private int ticketNumbers = 20;
public void run() {
while (true){
if(ticketNumbers<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"-->拿到了"+ticketNumbers--+"票");
}
}
public static void main(String[] args) {
TestThread4 testThread4 = new TestThread4();
new Thread(testThread4,"小明").start();
new Thread(testThread4,"小张").start();
new Thread(testThread4,"小王").start();
}
}
实现Callable接口
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
- 提交执行:Future result1 = ser.submit(1);
- 获取结果:boolean r1 = result1.get();
- 关闭服务:ser.shutdownNow();
总结:
Thread的底层实现原理就是静态代理模式:
- 真实对象和代理对象都要实现同一个接口
- 代理对象要代理真实角色
- 代理对象可以做很多真是对象做不了的事情
- 真实对象专注做自己的事情
Lambda表达式
函数式接口的定义
-
任何接口,如果指包含唯一一个抽象方法,那么它就是一个函数式接口。
-
对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。
public class TestLambda1 {
public static void main(String[] args) { ILove iLove = (int a) -> { System.out.println("i love you" + a); }; iLove.love(520); }
}
interface ILove {
void love(int a);
}
线程状态
线程停止
-
不推荐使用JDK提供的stop() destroy()方法,以废弃
-
推荐线程自己停止下来
-
建议使用一个标志位进行终止变量,当flag=false,则终止线程运行
public class TestStop implements Runnable {
//设置一个标志位 private boolean flag = true; @Override public void run() { int i = 0; while (flag) { System.out.println("run thread" + i++); } } //设置一个公开的方法停止线程 public void stop() { this.flag = false; } public static void main(String[] args) { TestStop testStop = new TestStop(); new Thread(testStop).start(); for (int i = 0; i < 200; i++) { System.out.println("main"+i); if(i==120){ //调用stop方法切换标志位,让线程停止 testStop.stop(); System.out.println("线程已停止"); } } }
}
线程休眠
- sleep指定当前线程阻塞的毫秒数
- sleep时间达到后线程进入就绪状态
- sleep可以模拟网络延迟,倒计时等
- 每个对象都有一把锁,sleep不会释放锁
Thlead.sleep(“指定的毫秒数”);
线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让cpu重新调度,礼让不一定成功,看cpu心情
Thread.yield();
Join
- Join合并线程,待此线程执行完成后,在执行其他线程,其他线程阻塞
- 可以想象为插队
Thread.join();
线程状态观测
线程可以处于以下状态之一:
- NEW
尚未启动的线程处于此状态 - RUNNABLE
在java虚拟机中执行的线程处于此状态 - BLOCKED
被阻塞等待监视器锁定的线程处于此状态 - WAITING
正在等待另一个线程执行特定动作的线程处于此状态 - TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态 - TERMINATED
已退出的线程处于此状态
Thread.State();
线程优先级
- Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
- 线程的优先级用数字表示,范围从1~10
- Thread.MIN_PRIORITY=1;
- Thread.MAX_PRIORITY=10;
- Thread.NORM_PRIORITY=5;
- 使用以下方式改变或获取优先级
getPriority() setPriority(int xxx)
注意:优先级的设定建议在start()调度前
优先级低只是意味着获取调度的概率低,并不是优先级低就不会被调用了,这都得看CPU的调度
守护(daemon)线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
setDaemon(true); //默认是false表示用户线程,正常的线程都是用户线程
线程同步
多个线程操作同一个资源
并发:同一个对象被多个线程同时操作
- 同步方法:public synchronized void method(int args){}
若将一个大的方法申明为synchronized将会影响效率 - 同步块:synchronized (obj) {}
- obj称之为同步监视器,但是推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身
死锁
多个线程互相抱着对方需要的资源,然后形成僵持
死锁避免方法
产生死锁的四个必要条件
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
只要破其中的任意一个或者多个条件就可以避免死锁发生
Lock锁
实例
class A{
private final ReentrantLock lock = new ReentrantLock();//ReentrantLock 可重入锁
public void method(){
lock.lock();
try{
//保证线程安全的代码
}
finally{
lock.unlock();
//如果同步代码有异常,要将unlock()写入finally语句块
}
}
}
synchronized与Lock的对比
- Lock是显示锁(手动开启和关闭锁),synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有很好的扩展性
转载:https://blog.csdn.net/Apandam/article/details/105306696