Java 多线程

Java 多线程

1、简介

​ 看起来同一时间多任务,实则同一时刻仍只在做同一件事情。

​ 程序、进程、线程

​ program、process、thread

​ 线程是CPU调度和执行的单位。进程是系统资源分配的单位。程序是指令和数据的有序集合。

2、创建

​ 创建方式有三种:

  1. Thread class (继承Thread类)
  2. Runnable (实现Runnable接口)
  3. Callable接口 (实现Callable接口)

线程开启不一定立即执行,由CPU来调度执行。

实现Thread类:

public class TestThread1 extends Thread{
    @Override
    public void run() {
        //run线程
        for(int i=0;i<20;i++){
            System.out.println("This is another thread!");
        }
    }
    public static void main(String[] args) {
        Thread thread=new TestThread1();
        //run()会直接先执行run()方法体,start()才会开辟一个新的线程走新方法
        thread.start();
        //thread.run();
        //主线程
        for(int i=0;i<20;i++){
            System.out.println("This is main thread!");
        }
    }
}

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

//多线程下载图片
public class TestThreadPic extends Thread{
    private String url;
    private String name;

    public TestThreadPic(String url,String name){
        this.url=url;
        this.name=name;
    }
    @Override
    public void run() {
        WebDownloader webDownloader=new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载的文件名为:"+name);
    }
    public static void main(String[] args) {
        TestThreadPic testThreadPic1=new
                TestThreadPic("https://hiimg.cqqnb.net/csimage/202207/01/1656662607274.jpg","1.jpg");
        TestThreadPic testThreadPic2=new
                TestThreadPic("https://hiimg.cqqnb.net/csimage/202207/01/1656662607274.jpg","2.jpg");
        TestThreadPic testThreadPic3=new
                TestThreadPic("https://hiimg.cqqnb.net/csimage/202207/01/1656662607274.jpg","3.jpg");
        testThreadPic1.start();
        testThreadPic2.start();
        testThreadPic3.start();
    }
}

class WebDownloader{
    //下载方法
    public void downloader(String url,String name)  {
        try{
            FileUtils.copyURLToFile(new URL(url),new File(name));
        }catch (IOException e){
            e.printStackTrace();
            System.out.print("IO异常");
        }
    }
}

下载的文件名为:2.jpg
下载的文件名为:3.jpg
下载的文件名为:1.jpg

实现Runnable接口:

推荐使用,避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用。

实现Callable接口:

可以抛出异常,可以定义返回值

实现Callable接口,需要返回值类型

重写call方法,需要抛出异常

创建目标对象

创建执行服务:

ExecutorServicer ser =Executors.newFixedThreadPool(1);

提交执行:

Future<Boolean> result1=ser.submit(t1);

获取结果:

boolean r1=result1.get();

关闭服务:

ser.shutdownNow();

3、静态代理

public class TestStaticProxy {
    public static void main(String[] args) {
        WeddingCompany weddingCompany=new WeddingCompany(new You());
        weddingCompany.HappyMarry();
    }
}

interface Marry{
    void HappyMarry();
}
//真实角色
class You implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("Happy Marry");
    }
}
//代理角色
class WeddingCompany implements Marry{
    private Marry target;
    public WeddingCompany(Marry marry){
        target=marry;
    }
    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();
        after();
    }
    private void before(){
        System.out.println("Before Marry");
    }
    private void after(){
        System.out.println("After Marry");
    }
}

特点:

  • ​ 代理对象与真实对象都实现同一个接口;
  • ​ 代理对象可以做真实对象做不了的事情;
  • ​ 真实对象专注实现核心方法(做自己的事)。

多线程中Thread类代理Runnable接口,类似。

4、Lambda表达式

避免匿名内部类明明过多

其实质属于函数式编程的概念

代码更简洁,去掉无意义代码,只留下核心逻辑

  • (params)->expression[表达式]
  • (params)->statement[语句]
  • (params)->{statements}

例:

new Thread(()->System.out.println("Thread Learning!")).start();

函数式接口:任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。(如Runnable接口中只有Run())。

public class TestLambda {
    //3.静态内部类
    static class Like2 implements ILike{
        @Override
        public void lambda() {
            System.out.println("I Like lambda2");
        }
    }

    public static void main(String[] args) {
        ILike like1=new Like1();
        like1.lambda();

        ILike like2=new Like2();
        like2.lambda();

        //4.局部内部类
        class Like3 implements ILike{
            @Override
            public void lambda() {
                System.out.println("I Like lambda3");
            }
        }
        ILike like3=new Like3();
        like3.lambda();

        //5.匿名内部类
        ILike like4=new ILike(){
            @Override
            public void lambda() {
                System.out.println("I Like lambda4");
            }
        };
        like4.lambda();

        //lambda简化
        ILike like5=()->{
            System.out.println("I Like lambda5")
        };
        like5.lambda();
    }
}

//1.定义一个函数式接口
interface ILike{
    void lambda();
}

//2.实现类
class Like1 implements ILike{
    @Override
    public void lambda() {
        System.out.println("I Like lambda1");
    }
}

5、线程状态

在这里插入图片描述

创建状态:

Thread t=new Thread();

就绪状态:

​ 当调用start()方法时,线程立即进入就绪状态,但不意味着立即运行。

运行状态:

​ 此时系统开始执行线程内的代码块。

阻塞状态:

​ 调用sleep(),wait()或同步锁定时,线程进入阻塞状态,阻塞事件解除后,重新进入就绪状态,等待CPU调度执行。

​ 每个对象都有一把锁,sleep()不会释放锁。

死亡状态:

​ 线程中断或结束。进入死亡状态的线程无法再次启动。

线程优先级:

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。

  • 线程的优先级用数字表示,范围从1~10。

    Thread.MIN_PRIORITY=1;
    Thread.MAX_PRIORITY=10;
    Thread.NORM_PRIORITY=5;
    
  • 使用以下方法改变或获取优先级:

    getPriority();
    setPriority(int);
    

优先级低的线程意味着获得调度的概率低,并不意味着优先级低就不被调用了。

6、守护(daemon)线程:

  • 线程分为用户线程和守护线程。
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不必等待守护线程执行完毕
  • 如,后台记录操作日志,监控内存,垃圾回收等待。
thread.setDaemon(true);

默认是用户线程,需自己设置守护线程。

7、线程同步

并发:同一对象被多个线程同时操作。

同步:线程同步其实是一种等待机制,多个需要同时访问某个资源的线程进入 资源的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

锁:保证线程安全。每个对象拥有一把锁。synchronized,但一个线程获得资源的排他锁,独占资源,其他线程必须等待,使用后释放锁即可,存在以下问题:

  • 一个线程持有锁会导致其他需要此锁的线程挂起;
  • 在多线程竞争下,加锁、释放锁会导致较多的上下文切换和调度延时,引起性能问题;
  • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。
package syn;

public class UnSafeBuyTic {
    public static void main(String[] args) {
        BuyTicket buyTicket=new BuyTicket();

        new Thread(buyTicket,"张三").start();
        new Thread(buyTicket,"李四").start();
        new Thread(buyTicket,"王五").start();
    }
}

class BuyTicket implements Runnable{
    private int ticketNum=10;
    boolean flag=true;
    @Override
    public void run() {
        while (flag){
            try{
            	Thread.sleep(100);
        	}catch (Exception e){
            	e.printStackTrace();
        	}
            buy();
        }
    }
    //synchronized关键字加锁
    private synchronized void buy(){
        if(ticketNum<=0){
            flag=false;
            return;
        }
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNum--);
    }
}

8、死锁

8.1、死锁

​ 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或多个线程都在等待对方释放资源,都停止执行的情形,被称为死锁。某一个同步块同时拥有两个以上对象的锁时,就可能发生死锁问题。

在这里插入图片描述

8.2、Lock锁

​ 显式定义同步锁实现同步,是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应获得Lock对象。

​ ReentrantLock类实现了Lock接口,拥有与Synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁。

ReentrantLock.lock();

ReentrantLock.unLock();

使用Lock锁,JVM将花费较少的时间调度线程,性能更好,并具有更好的扩展性。

Lock>同步代码块(已经进入方法体,分配了相应资源)>同步方法(在方法体之外)

9、线程协作

9.1、生产者消费者问题

方法名作用
wait()表示线程会一直等待,直到其他线程通知,与sleep()不同,会释放锁
wait(long)指定等待的毫秒数
notify()唤醒一个处于等待状态的线程
notifyAll()唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

只能在同步方法与同步代码块内使用。

9.1.1管程法
9.1.2信号灯法

10、线程池

//建池
ExecutorService service=Executors.newFixedThreadPool(10);
//添加线程
service.execute(new Thread());
//关闭连接
service.shutdown();

一直等待,直到其他线程通知,与sleep()不同,会释放锁 |
| wait(long) | 指定等待的毫秒数 |
| notify() | 唤醒一个处于等待状态的线程 |
| notifyAll() | 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度 |

只能在同步方法与同步代码块内使用。

9.1.1管程法
9.1.2信号灯法

10、线程池

//建池
ExecutorService service=Executors.newFixedThreadPool(10);
//添加线程
service.execute(new Thread());
//关闭连接
service.shutdown();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值