java多线程(简洁易上手)

线程创建

线程创建的三种方式:

1.Thread类,继承Threadlei

2.Runable接口,实现Runable接口

3.Callable接口,实现Callable接口


Thread类

Thread类实现线程:

1.自定义线程继承Thread类

2.重写run方法,编写线程执行体

3.创建线程对象,调用start()方法启动线程


代码展示:

 调用start方法,main主线程和run方法线程同时进行,交叉执行

调用run方法,先执行run方法线程,再调用main主方法


多线程案例:网图下载

先下载commons io库

 代码:

import org.apache.commons.io.FileUtils;

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

//练习Thread,实现线程同步下载图片
public class TestThread1 extends Thread {
 private String url;
 private String name;

    @Override
    public void run() {
        WebDownloader webDownloader=new WebDownloader();
        try {
            webDownloader.downloader(url,name);
            System.out.println("下载文件名为:"+name);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public TestThread1(String url, String name){
     this.url=url;
     this.name=name;
 }

    public static void main(String[] args) {
        TestThread1 testThread1=new TestThread1("https://blog.kuangstudy.com/usr/them/handsome/usr/img/sj/1.jpg","2.jpg");
        TestThread1 testThread2=new TestThread1("https://blog.kuangstudy.com/usr/them/handsome/usr/img/sj/2.jpg","3.jpg");
        TestThread1 testThread3=new TestThread1("https://blog.kuangstudy.com/usr/them/handsome/usr/img/sj/3.jpg","4.jpg");

        testThread1.start();
        testThread2.start();
        testThread3.start();
    }
}

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

Runnable接口

实现Runnable接口:

1.实现接口

2.启动线程:传入目标对象+Thread对象.start()

public class TextRunnable implements Runnable {
    @Override
    public void run() {
        for(int i=0;i<5;i++){
            System.out.println("好好学习");
        }
    }

    public static void main(String[] args) {
        //创建Runnable接口实现的类对象
        TextRunnable textRunnable=new TextRunnable();
        //创建线程对象,通过线程对象来打开线程
        new Thread(textRunnable).start();
        for(int j=0;j<5;j++){
            System.out.println("天天向上");
        }
    }
}

Runanble接口和Thread都可以实现线程,Thread可以直接实现开启线程,而Runnable接口要借助Thread传入参数开启。


Callable接口

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

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

3.创建目标

4.创建执行服务:ExecutorService ser=Executors.newFixedThreadPool(1);

5.提交执行:Future<Boolean> result1=ser.submit(1)

6.获取结果:boolean r1=result1.get()

7.关闭服务:ser.shutsdownNow()

public class TextRunnable implements Callable<Boolean> {
    @Override
    public Boolean call() throws Exception {
        System.out.println("好好学习");
        return true;
    }

    public static void main(String[] args) throws Exception {
        TextRunnable textRunnable=new TextRunnable();
        TextRunnable textRunnable1=new TextRunnable();
//       4.创建执行服务:
        ExecutorService ser= Executors.newFixedThreadPool(2);  //创建两个线程池
//5.提交执行:
        Future<Boolean> result1=ser.submit(textRunnable);    //提交服务得到一个返回值
        Future<Boolean> result2=ser.submit(textRunnable1);
//6.获取结果:
        boolean r1=result1.get();
        boolean r2=result2.get();
//7.关闭服务:
        ser.shutdown();  //shutdownNow();
    }
}

 Lambda表达式

函数式接口的定义:

·任何接口,如果只包含唯一一个抽象方法,那么它就是函数接口

        public interface Runnable{

                public abstract void run();

}

·对应函数式接口,我们可以通过lamda表达式来创建接口的对象

public class Textlambda {
    //3.静态内部类
    static  class like2 implements Ilike{

        @Override
        public void lambda() {
            System.out.println("这是静态内部类方法");
        }
    }
    public static void main(String[] args) {
        Ilike like=new like();
        like.lambda();//函数式接口
        like=new like2();
        like.lambda();//静态内部类
        //4.局部内部类
        class like3 implements Ilike{

            @Override
            public void lambda() {
                System.out.println("这是局部内部类方法");
            }
        }
        like=new like3();
        like.lambda();
        //5.匿名内部类,没有类的名称,必须借助接口或父类
        like=new Ilike() {
            @Override
            public void lambda() {
                System.out.println("这是匿名内部类");
            }
        };
        like.lambda();
        //6.lamdba方法
        like = ()->{
          System.out.println("这是lambda方法");
        };
        like.lambda();
    }
}

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

//2.实现类
class like implements Ilike{

    @Override
    public void lambda() {
        System.out.println("这是lambda方法");
    }
}

线程状态

 线程方法:

线程方法
方法说明
setPriority(int newPriority)更改线程的优先级
static void sleep(long millis)在指定的毫秒内让当前正在执行的线程休眠
void join()等待该线程终止
static void yield()暂停当前正在执行的线程对象,并执行其他线程
void interrupt()中断线程,别用这个方式
boolean isAlive()测试线程是否处于活动状态

线程停止:

//1.建议线程正常停止,利用次数,不建议用死循环
//2.建议用标志位
//3.不要使用stop或者destroy等过时或者JDK不建议使用的方法
public class Textlambda implements Runnable{
private boolean flag=true;   //设置标识位
    @Override
    public void run() {
        int i=0;
        while(flag){
        System.out.println("线程"+i++);
    }
    }
    public void stop(){  //停止线程的方法
        this.flag=false;
    }

    public static void main(String[] args) {
        Textlambda textlambda=new Textlambda();
        new Thread(textlambda).start();
        for(int i=0;i<500;i++){
            System.out.println("main"+i);
            if(i==400){   //调用stop方法切换标志位
                textlambda.stop();
                System.out.println("线程停止");
            }
        }
    }
}

线程休眠sleep

·sleep(时间)指定当前线程阻塞的毫秒数

·sleep存在异常InterrupttedException

·sleep时间达到后线程进入就绪状态

·sleep可以模拟网络延迟等

·每一个对象都有一个对象锁,sleep不会释放锁

public class Textlambda {
    public static void main(String[] args) {
        try {
            testsleep();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    public static void  testsleep() throws InterruptedException {
        int num=10;
       while(true){
           Thread.sleep(1000);
 System.out.println(num--);
 if(num<=0){
     break;
 }
        }
    }
}

线程礼让yield

·礼让线程,让当前正在执行的线程暂停,但不阻塞

·将线程从运行状态转为就绪状态

·让CPU重新调度,礼让不一定成功

public class Textlambda {
    public static void main(String[] args) {
        yield yield=new yield();
        new Thread(yield,"线程1").start();
        new Thread(yield,"线程2").start();
    }
}
class yield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}

join

join合并线程,待次线程执行完成后,再执行其他线程,其他线程阻塞

public class Textlambda implements Runnable{
    @Override
    public void run() {
        for (int i=0;i<100;i++){
            System.out.println("线程1"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Textlambda textlambda=new Textlambda();
        Thread thread=new Thread(textlambda);
        thread.start();
        for (int i=0;i<200;i++){
            System.out.println("main线程"+i);
            if(i==50){
                thread.join();  //线程礼让
            }
        }
    }
}

线程状态观察Thread.State

·NEW        线程尚未启动

·RUNNABLE        线程在java虚拟机中执行

·BLOCKED        线程被阻塞等待监视器锁定

·WAITING        线程正在等待另一个线程执行待定动作

·TIMED_WAITING        线程正在等待另一个线程执行动作达到指定时间

·TERMINATED        线程退出

public class Textlambda {
    public static void main(String[] args) throws InterruptedException {
       Thread thread=new Thread(()->{
           for(int i=0;i<5;i++){
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
           System.out.println("线程就将停止");
       });
      //观察状态
        Thread.State state=thread.getState();
        System.out.println(state);  //new
        //观察启动后
        thread.start();
        state=thread.getState();
        System.out.println(state); //Run

        while (state!=Thread.State.TERMINATED){
            Thread.sleep(1000);
            state=thread.getState();//不断更新状态
            System.out.println(state);
        }
    }

}

线程优先级

线程优先级,1~10

Thread.MIN_PRIORITY=1

Thread.MAX_PRIORITY=10

Thread.NORM_PRIORITY=5

改变优先级和获取优先级:getPriority()        setPriority(int num)

public class Textlambda {
    public static void main(String[] args) {
        //主线程,默认优先级5
        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);
        t2.setPriority(2);
        t3.setPriority(Thread.MIN_PRIORITY);
        t4.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
class mypriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"->"+Thread.currentThread().getPriority());
    }
}

守护线程daemon

·线程分为用户线程和守护线程

·虚拟机必须保证用户线程执行完毕

·虚拟机不需要等待守护线程执行完毕

public class Textlambda {
    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<3600;i++){
            System.out.println("活着");
        }
        System.out.println("死亡");
    }
}

线程同步

·由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放资源锁,但会产生如下问题:

1.一个线程持有锁会导致其他需要该锁的线程挂起

2.在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延迟,引起性能问题

3.如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置


同步方法:

·synchronized方法

public synchronized void method(int args){}

synchronized方法控制对象的访问,每一个对象对应一把锁,每个synchronized方法都必须获得调用该方法的锁才可以执行,否则线程会被阻塞,方法一旦执行,就独占该锁,直到该方法改回才释放锁,后面被阻塞的线程获得这个锁才可以执行。

public class Textlambda {
    public static void main(String[] args) {
        BuyTicket buyTicket=new BuyTicket();
        new Thread(buyTicket,"消费者1").start();
        new Thread(buyTicket,"消费者2").start();
        new Thread(buyTicket,"黄牛").start();
    }
}

class BuyTicket implements Runnable{
private int ticketNums=10;   //票数
boolean flag=true;     //外部停止方法
    @Override
    public void run() {
        //买票
        while(flag){
            try {
                buy();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    //synchronixed方法
    private synchronized void buy() throws InterruptedException {
        if(ticketNums<=0){   //判断是否有票
            flag=false;
            return;
        }
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
    }
}

同步块

·synchronized块

synchroni(Obj){}

Obj称之为同步监视器,Obj可以是任何对象,但是推荐使用共享资源作为同步监视器,同步方法中无须指定同步监视器,因为同步方法的监视器就是this,就是这个对象本身。

 同步监视器的执行过程:

1.第一个线程访问,锁定同步监视器,执行其代码

2.第二个线程访问,发现同步监视器被锁定,无法访问

3.第一个线程访问结束,解锁同步监视器

4.第二个线程访问,发现同步监视器没有锁,任何锁定并访问


死锁

产生死锁的四个必要条件:

1.互斥条件:一个资源每次只能被一个进程使用。

2.请求与保存条件:一个进程因请求资源而阻塞时,对已获得的资源保存不放。

3.不剥夺条件:进程获得的资源,在未使用完之前,不能强行剥夺

4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

public class Textlambda {
    public static void main(String[] args) {
Makeup makeup=new Makeup(0,"琳琳");
        Makeup makeup1=new Makeup(1,"娜娜");
        makeup.start(); 
        makeup1.start();
    }
}
//口红
class Lipstick{

}
//镜子
class Mirror{

}
class Makeup extends Thread{
    //需要的资源只有一份,用static来保证只有一份
    static Lipstick lipstick=new Lipstick();
    static Mirror mirror=new Mirror();
    int choice;//选择
    String girlname;
    Makeup(int choice,String girlname){
        this.choice=choice;
        this.girlname=girlname;
    }
    @Override
    public void run() {
        //化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    //化妆方法,互相持有对方的锁,就是拿到对方的资源
    private void makeup() throws InterruptedException {
        if (choice==0){
            synchronized (lipstick){ //获得口红的锁
                System.out.println(this.girlname+"获得口红的锁");
                Thread.sleep(1000);
                synchronized (mirror){  //一秒钟后获得镜子
                    System.out.println(this.girlname+"获得镜子的锁");
                }
            }
        }else {
            synchronized (mirror){ //获得镜子的锁
                System.out.println(this.girlname+"获得镜子的锁");
                Thread.sleep(2000);
                synchronized (lipstick){  //一秒钟后获得口红
                    System.out.println(this.girlname+"获得口红的锁");
                }
        }
    }
}}

认识,获得一个锁就得不到另一个锁


 Lock锁

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

public class Textlambda {
    public static void main(String[] args) {
        Textlock textlock=new Textlock();
        new Thread(textlock,"1").start();
        new Thread(textlock,"2").start();
        new Thread(textlock,"3").start();
    }
}
class Textlock implements Runnable{
int ticketnum=10;
//定义lock锁
    private 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(ticketnum--);
               } else {
                   break;
           }
       }finally {
           lock.unlock();
       }

}}}

synchronized与Lock的对比:

1.Lock是显式锁(需要手动开启和关闭锁),synchronized是隐式锁,出了作用域自动关闭

2.Lock只有代码块锁,synchronized有代码块锁和方法锁

3.优先使用:Lock>同步代码块>同步方法


线程通信:

线程通信方法:

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

生产者消费者模式:(管程法)(信号灯法)

·生产者:负责产生数据

·消费者:负责处理数据

·缓冲区:消费者不能直接使用生产者的数据,他们之间有一个缓冲区

 管程法:

public class Textlambda {
    public static void main(String[] args) {
        syncontainer container=new syncontainer();
        new productor(container).start();
        new consumber (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 consumber extends Thread{
    syncontainer container;
    public consumber(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) {
                throw new RuntimeException(e);
            }
        }
        //如果没有满。我们就需要丢入产品
        chickens[count]=chicken;
        count++;
        //可以通知消费者消费了
        this.notifyAll();
    }
    //消费者消费产品
    public synchronized chicken pop(){
        //判断能否消费
        if(count==0){
            try{
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //等待生产者生产,消费者等待
        }
        //如果可以消费
        count--;
        chicken chicken=chickens[count];
        //吃完了,通知生产者生产
        this.notifyAll();
        return chicken;
    }
}

信号灯法:

public class Textlambda {
    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%20==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{
    //演员表演,观众等待
    //观众观看,演员等待
    String voice;//表演的节目
    boolean flag=true;
    //表演
    public synchronized void play(String voice){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("演员表演了:"+voice);
        //通知观众观看
        this.notifyAll();//通知唤醒
        this.voice=voice;
        this.flag=!this.flag;
    }
    //观看
    public synchronized void watch()  {
        if(flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("观看了:"+voice);
    //通知演员表演
        this.notifyAll();
        this.flag=!this.flag;
    }

}

线程池

背景:经常创建和销毁,使用量大的资源,对性能影响大

思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完发回池中。

线程池API:ExecutorService和Executors

ExecutorServoce:真正到线程池接口

        ·void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable

        ·void shutdown():关闭线程池

        ·<T>Future<T>submint(Callable<T>task):执行任务,有返回值,一般用来执行Callable

Executors:工具类,线程池的工厂类,用来创建并返回不同类型的线程池


public class Textlambda {
    public static void main(String[] args) {
        //1.创建服务,创建线程池,参数为线程池大小
        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());
    }
}

线程池。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前段被迫创业

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值