多线程
概念
程序:指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
进程:计算机中的程序关于某数据集合上的一次运行活动,是一个动态的概念,是系统资源分配的单位
线程:CPU调度和执行的最小单位。一个进程可以包含若干个线程,一个进程中至少有一个线程,不然没有存在的意义
多线程:真正的多线程是指有多个CPU,即多核
线程创建
继承Thread类(重点)
不建议使用:避免OOP单继承局限性
//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
public class Demo01 extends Thread{
//run方法,线程体
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我在跑线程----"+i);
}
}
//main 线程,主线程
public static void main(String[] args) {
//创建一个线程对象
Demo01 demo01 = new Demo01();
//调用start()方法开启线程
demo01.start();
for (int i = 0; i < 700; i++) {
System.out.println("我是主程序----"+i);
}
}
}
注意:线程开启不一定立即执行,由CPU调度
实现Runnable接口(核心)
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
//创建线程方式二:实现runnable,重写run()方法,执行线程需要丢入runnable接口实现类,调用start方法
public class Demo03 implements Runnable{
@Override
public void run() { //run方法,线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在跑线程----"+i);
}
}
public static void main(String[] args) { //main 线程,主线程
//创建runnable接口的实现类对象
Demo03 demo03 = new Demo03();
//创建线程对象,通过线程对象开启线程
new Thread(demo03).start();
for (int i = 0; i < 700; i++) {
System.out.println("我是主程序----"+i);
}
}
}
实现Callable接口(了解)
callable好处:
- 可以定义返回值
- 可以抛出异常
//线程创建方式三:实现callable接口
public class Demo05 implements Callable <Boolean>{
private String url;
private String name;
public Demo05(String url,String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载的文件名为:"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Demo05 demo021 = new Demo05("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2151136234,3513236673&fm=26&gp=0.jpg","0.jpg");
Demo05 demo022 = new Demo05("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3791918726,2864900975&fm=26&gp=0.jpg","1.jpg");
Demo05 demo023 = new Demo05("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3681880960,455182084&fm=26&gp=0.jpg","2.jpg");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(demo021);
Future<Boolean> r2 = ser.submit(demo022);
Future<Boolean> r3 = ser.submit(demo023);
//获取结果
boolean rs1 = r1.get();
boolean rs2 = r1.get();
boolean rs3 = r1.get();
System.out.println(rs1);
System.out.println(rs2);
System.out.println(rs3);
//关闭服务
ser.shutdown();
}
}
多个线程操作同一对象
//多个线程同时操作同一对象
//问题:多个线程操作同一个资源,线程不安全,数据紊乱
public class Demo04 implements Runnable{
private int ticket = 10;//票数
@Override
public void run() {
while (true){
if (ticket<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticket--+"张票");
//模拟延迟
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Demo04 demo04 = new Demo04();
new Thread(demo04,"小明").start();
new Thread(demo04,"小红").start();
new Thread(demo04,"黄牛").start();
}
}
静态代理
静态代理模式:
- 真实对象和代理对象都要实现同一个接口
- 代理对象要代理真实角色
优点:
- 代理对象可以做很多真实对象做不了的事情
- 真实对象专注做自己的事情
Lambda表达式
函数式接口(Functional Interface):任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
Lambda表达式:
- Lambda表达式只能有一行代码的情况下才能简化为一行,如果有多行,那么就用代码块包裹
- 使用Lambda表达式的前提是接口是函数式接口
- 多个参数也可以去掉参数类型,要去掉就同时去掉,必须加上括号
Lambda表达式推导:
public class Demo06 {
//3.静态内部类
static class Like2 implements ILike{
@Override
public void like() {
System.out.println("I Love You!");
}
}
public static void main(String[] args) {
ILike like = new Like1();//实现类
like.like();
like = new Like2();//静态内部类
like.like();
//4.局部内部类
class Like3 implements ILike{
@Override
public void like() {
System.out.println("I Love You!");
}
}
like = new Like3();
like.like();
//5.匿名内部类,没有类的名称,必须借助接口或者父类
like = new ILike() {
@Override
public void like() {
System.out.println("I Love You!");
}
};
like.like();
//6.Lambda表达式简化
//like = ()-> System.out.println("I Love You!");
like = ()->{
System.out.println("I Love You!");
};
like.like();
}
}
//1.定义一个函数式接口
interface ILike{
void like();
}
//2.实现类
class Like1 implements ILike{
@Override
public void like() {
System.out.println("I Love You!");
}
}
线程常用方法
线程停止
- 不推荐使用 JDK 提供的 stop()、destroy() 方法,已废弃
- 推荐线程自己停止下来
- 建议使用一个标志位进行终止变量,当 flag = false,则终止线程运行
//线程停止
/*
1.建议线程正常停止:利用次数,不建议死循环
2.建议使用标志位:设置一个标志位
3.不要使用stop或destroy等过时或JDK不建议使用的方法
*/
public class Demo01 implements Runnable{
private boolean flag = true;
@Override
public void run() {
while (flag) {
System.out.println("Thread----运行中");
}
}
public void stopThread(){
this.flag = false;
}
public static void main(String[] args) {
Demo01 demo01 = new Demo01();
new Thread(demo01).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main运行中"+i);
if(i==990){
demo01.stopThread();
System.out.println("线程停止了");
}
}
}
}
线程休眠
- sleep(时间) 指定当前线程阻塞的毫秒数
- sleep 存在异常
- sleep 时间达到后线程进入就绪状态
- sleep 可以模拟网络延迟,倒计时等
- 每一个对象都有一个锁,slee 不会释放锁
public static void main(String[] args) {
try {
down();
} catch (InterruptedException e) {
e.printStackTrace();
}
//打印当前时间
Date startTime = new Date(System.currentTimeMillis());
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//模拟倒计时
public static void down() throws InterruptedException {
int num = 10;
while (true){
Thread.sleep(1000);
System.out.println(num--);
if(num<=0){
break;
}
}
}
线程礼让
礼让不一定成功,看CPU调度
public class Demo03 {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"A").start();
new Thread(myYield,"B").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"结束执行");
}
}
线程插队 Join
Join:合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("VIP插队的"+i);
}
}
public static void main(String[] args) throws InterruptedException {
Demo04 demo04 = new Demo04();
Thread thread = new Thread(demo04);
thread.start();
for (int i = 0; i < 500; i++) {
System.out.println("main"+i);
if (i==250){
thread.join();
}
}
}
线程状态
- NEW:尚未启动的线程
- RUNNABLE:在 Java 虚拟机中执行的线程
- BLOCKED:被阻塞等待监视器锁定的线程
- WAITING:正在等待另一个线程执行特定动作的线程
- TIMED_WAITING:正在等待另一个线程执行动作达到指定等待时间的线程
- TERMINATED:已退出的线程
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 2; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("-----------");
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state);
//启动后
thread.start();
state = thread.getState();
System.out.println(state);
while (state != Thread.State.TERMINATED){
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
注意:线程终端或者结束,一旦进入死亡状态,就不能再次启动
线程优先级
优先级的高低意味着获得调度的概率高低,并不是优先级低就不会被调用,还是要看CPU的调度
建议优先级的设定在 start() 前
public class Demo05 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
t1.start();
t2.setPriority(Thread.MAX_PRIORITY);
t2.start();
t3.setPriority(Thread.MIN_PRIORITY);
t3.start();
t4.setPriority(4);
t4.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
}
}
守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
public class Demo06 {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true);//默认是false,表示是用户线程,正常的线程都是用户线程
thread.start();
new Thread(you).start();
}
}
//上帝
class God implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("上帝保佑着你");
}
}
}
//你
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("活着");
}
System.out.println("------goodbye world-------");
}
}
锁机制
线程同步
并发:同一个对象被多个线程同时操作
线程同步:线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕,下一个线程再使用
synchronized(隐式锁)
锁机制:当一个线程获得一个对象的排它锁时,独占该资源,其他线程必须等待,线程使用后释放锁
同步方法:synchronized 方法控制对”对象“的访问,每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行
public synchronized void method(int args){
//代码
}
同步块:
synchronized(Obj){
//代码
}
Obj 称为同步监视器:
- Obj 可以是任何对象,但是推荐使用共享资源作为同步监视器。锁的对象是变化的量,需要增删改的对象。
- 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是 class
死锁
死锁:多个线程各自占有一些共享资源,并且互相等待其他线程占有资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情况
产生死锁的四个必要条件(只要破解其中一个或多个条件就可以避免死锁发生):
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
Lock锁
Lock锁:提供了对共享资源的独立访问,每次只能有一个线程对 Lock 对象加锁,线程开示访问共享资源之前应获得 Lock 对象
ReentrantLock(可重入锁):实现了 Lock,它拥有与 synchronized 相同的并发性和内存语义,可以显式加锁、释放锁
public class Demo07 {
public static void main(String[] args) {
TestLock testLock = new TestLock();
new Thread(testLock).start();
new Thread(testLock).start();
new Thread(testLock).start();
}
}
class TestLock implements Runnable{
int ticketNum = 10;
//定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();//加锁
if (ticketNum>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->"+ticketNum--);
}else {
break;
}
}finally {
//解锁
//如果同步代码有异常,要将unlock()写入finally语句块
lock.unlock();
}
}
}
}
synchronized 与 Lock 对比
-
Lock 是显示锁(手动开启和关闭锁,别忘记关闭锁),synchronized 是隐式锁,出了作用域自动释放
-
Lock 只有代码块锁,synchronized 有代码块锁和方法锁
-
使用 Lock 锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类)
-
优先使用顺序:
Lock > 同步代码块 > 同步方法
线程协作
生产者消费者模型:线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
线程通信方法:
- wait():表示线程一直等待,直到其他线程通知,与 sleep 不同,会释放锁
- wait(long timeout):指定等待的毫秒数
- notify():唤醒一个处于等待状态的线程
- notifyAll():唤醒同一个对象上所有调用 wait() 方法的线程,优先级高的线程优先调度
注意:只能在同步方法或者同步代码块中使用
管程法
//测试:生产者消费者模型--->利用缓冲区解决:管程法
//生产者,消费者,产品,缓冲区
public class Demo08 {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
//生产者
class Productor extends Thread{
SynContainer container;
public Productor( SynContainer container){
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer( SynContainer container){
this.container = container;
}
//消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了--->"+container.pop().id+"只鸡");
}
}
}
//产品
class Chicken{
int id;//产品编号
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer{
//容器大小
Chicken[] chickens = new Chicken[10];
int count = 0;//容器计数器
//生产者放入产品
public synchronized void push(Chicken chicken){
//如果容器满了,就需要等待消费者消费
if (count==chickens.length){
//通知消费者,生产等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满,就需要丢入产品
chickens[count] = chicken;
count++;
//可以通知消费者消费
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop(){
//判断能否消费
if (count==0){
//等待生产者生产,消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
count--;
Chicken chicken = chickens[count];
//通知生产者生产
this.notifyAll();
return chicken;
}
}
信号灯法
//测试:生产者消费者问题--->信号灯法,标志位解决
public class Demo09 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生产者--->演员
class Player extends Thread{
TV tv;
public Player(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i%2==0){
this.tv.play("新闻联播");
}else {
this.tv.play("脑白金");
}
}
}
}
//消费者--->观众
class Watcher extends Thread{
TV tv;
public Watcher(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//产品--->节目
class TV{
//演员表演,观众等待 T
//观众观看,演员等待 F
String readio;//表演的节目
boolean flag = true;
//表演
public synchronized void play(String readio){
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了:"+readio);
//通知观众观看
this.notifyAll();
this.readio = readio;
this.flag = !flag;
}
//观看
public synchronized void watch(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看了:"+readio);
//通知演员表演
this.notifyAll();
this.flag = !flag;
}
}
线程池
线程池相关API:ExecutorService 和 Executors
ExecutorService:真正的线程池接口
- void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
Future submit(Callable task):执行任务,有返回值,一般用来执行Callable - void shutdown():关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
public class Demo01 {
public static void main(String[] args) {
//1.创建服务,创建线程池
//newFixedThreadPool 参数为线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//2.关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}

浙公网安备 33010602011771号