java多线程编程详细介绍

线程

线程简单介绍

程序:程序是指令和数据的有序集合,它是静态的,没有运行的含义。

进程:进程就是执行程序的过程,它是动态的。是系统资源分配器的单位。

线程:进程可以分成多个单元同时运行,这些再被进程分配的多个单元叫做线程。线程是cpu调度的最小单位。

进程控制块:控制进程。里面存放进程的信息。

下面是关系图:
在这里插入图片描述
实际上,一个CPU,同一时刻只能执行一个线程。但是CPU运算速度很快,达到纳秒级别,我们在宏观上是多个任务同时执行,即并行。

多任务:同时执行多个任务。例如:边做什么边做什么。

多线程:多个线程同时执行。

线程的创建

三种创建线程的方式:继承Thread类、实现Runnable接口、实现Callable接口。

Thread类

该类实现了Runnable接口。

继承Thread类创建线程

创建步骤:

  • 自定义一个类
  • 继承Thread类
  • 重写run方法
  • 调用start方法,启动线程。
public class MyThread1 extends Thread {
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 200; i++) {
    
    
            System.out.println("我是线程1--"+ i);
        }
    }

    public static void main(String[] args) {
    
    
        //创建一个线程对象
        MyThread1 myThread1 = new MyThread1();

        //开启线程
        myThread1.start();

        //主线程
        for (int i = 0; i < 2000; i++) {
    
    
            System.out.println("我是主线程--" + i);
        }
    }
}

实现Runnable接口创建线程(推荐)

  • 实现Runnable接口
  • 重写run方法。
  • 执行线程需要丢入runnable接口的实现类,调用start方法
//创建线程方式2:实现Runnable接口,重写run,执行线程需要传入runnable接口实现类,调用start()方法
public class TestThread3 implements Runnable{
    
    
    @Override
    public void run() {
    
    
        //run方法线程体
        for(int i = 0; i < 200; i++){
    
    
            System.out.println("线程1正在运行-------"+i);
        }

    }
    public static void main(String[] args){
    
    
        //创建线程1
        TestThread3 myThread = new TestThread3();
        //开启线程
        new Thread(myThread).start();
        for(int i = 0; i < 2000; i++){
    
    
            System.out.println("我是主线程正在运行----"+i);
        }
    }
}

模拟龟兔赛跑

//赛跑
public class Race implements Runnable{
    
    

    //胜利者
    private static String winner;
    
    @Override
    public void run() {
    
    
        //跑道为1000步,如果谁先跑1000步谁就赢
        for (int i = 0; i <= 1000; i++) {
    
    
            //模拟兔子睡觉,每10步兔子睡一次觉
            if(Thread.currentThread().getName().equals("兔子")&& i%10 == 0 ) {
    
    
                try {
    
    
                   Thread.sleep(10);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            //判断比赛是否结束
            boolean flag = gameOver(i);
            if(flag){
    
    
                break;
            }
            //打印谁跑多少步
            System.out.println(Thread.currentThread().getName()+"----跑了"+i+"步");
        }
    }
    //判断胜利者
    private boolean gameOver(int steps) {
    
    

        if (winner != null) {
    
    
            return true;
        } else {
    
    
            if (steps >= 1000) {
    
    
                winner = Thread.currentThread().getName();
                System.out.println(winner + "win");
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
    
    
        Race race = new Race();
		//比赛开始
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

实现Callable接口创建线程

实现步骤:

  • 实现callable接口,需要返回值类型
  • 重写call方法,需要抛出异常
  • 创建目标对象
  • 创建执行服务
  • 提交服务
  • 获取服务
  • 关闭服务

图片下载案例:

//线程创建方式3,
public class TestCallable implements Callable<Boolean> {
    
    
    private String url;
    private String name;

    public TestCallable(String url, String name){
    
    
        this.url = url;
        this.name = name;
    }
    @Override
    public Boolean call() {
    
    
        WebDownloader1 webDownloader = new WebDownloader1();
        webDownloader.downDownload(url,name);
        System.out.println("下载文件名:"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        //创建目标对象
        TestCallable testThread1 = new TestCallable("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1601280086&di=2572142a87268b8c40db22edb56399d8&src=http://a3.att.hudong.com/14/75/01300000164186121366756803686.jpg","1.jpg");
        TestCallable testThread2 = new TestCallable("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1601280086&di=2572142a87268b8c40db22edb56399d8&src=http://a3.att.hudong.com/14/75/01300000164186121366756803686.jpg","2.jpg");
        TestCallable testThread3 = new TestCallable("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1601280086&di=2572142a87268b8c40db22edb56399d8&src=http://a3.att.hudong.com/14/75/01300000164186121366756803686.jpg","3.jpg");
        
        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);

        //提交执行
        Future<Boolean> result1 = ser.submit(testThread1);
        Future<Boolean> result2 = ser.submit(testThread2);
        Future<Boolean> result3 = ser.submit(testThread3);

        //获取结果
        boolean rs1 = result1.get();
        boolean rs2 = result2.get();
        boolean rs3 = result3.get();
        System.out.println(rs1+"-"+rs2+"-"+rs3);
        //关闭服务
        ser.shutdownNow();


    }
}

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

静态代理

静态代理模式:

  • 目标对象和代理对象都要实现同一个接口

  • 代理对象要代理目标对象

好处:代理对象可以做目标对象做不了的东西,而目标对象可以只关注自己的事情

结婚代理案例:

public class StaticProxy {
    
    


    public static void main(String[] args) {
    
    
        //委托人
        You you =new You();
        //代理人
        WeddingCompany weddingCompany = new WeddingCompany(you);
        //执行代理方法
        weddingCompany.happyMarry();

    }
}
//结婚接口
interface Marry{
    
    
    //结婚
    void happyMarry();
}

//结婚对象
class You implements Marry{
    
    
    //某人结婚
    @Override
    public void happyMarry() {
    
    
        System.out.println("某人结婚");
    }
}
//婚庆公司
class WeddingCompany implements Marry{
    
    
    //委托人,代理对象
    private Marry target;

    public WeddingCompany(Marry target) {
    
    
        this.target = target;
    }

    //举办婚礼
    @Override
    public void happyMarry() {
    
    
        System.out.println("布置现场");
        this.target.happyMarry();
        System.out.println("收拾现场");
    }
}

线程的五大状态

线程的五大状态:创建状态,就绪状态、运行状态、阻塞状态、死亡状态。

线程停止

  • 建议线程正常停止,利用次数,不建议死循环。
  • 简历使用标志位,设置标志位。
  • 不建议使用stop,destroy方法。

线程休眠

  • sleep指定当前线程阻塞的毫秒数
  • sleep存在异常InterruptedException
  • sleep时间到达后线程进入就绪状态
  • sleep可以模拟网络延时,倒计时等。
  • 每一个对象都是锁,sleep不会释放锁

休眠案例:

public class TestSleep2 implements Runnable {
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println("倒计时:"+i);
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
    
    
        //10秒倒计时
        new Thread(new TestSleep2()).start();
        //获取系统当前时间
        Date startTime = new Date(System.currentTimeMillis());
        while (true){
    
    
            try {
    
    
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                Thread.sleep(1000);
                startTime = new Date(System.currentTimeMillis());
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }

        }
    }
}

线程礼让

  • 礼让方法:yield()

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

  • 让cpu重新调度,礼让不一定成功

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

线程强制执行——join

public class TestJoin implements Runnable{
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        for (int i = 0; i < 1000; i++) {
    
    
            if(i == 200){
    
    
                thread.join();
            }
            System.out.println("main");
        }
    }


    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            System.out.println("线程插队");
        }
    }
}

观察测试线程状态

用getState()获取当前线程的状态。
在这里插入图片描述
在这里插入图片描述

线程的优先级

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

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

  • getPriority()和setPriority(int xxx)用来设置和获取优先级

    注意:先设置再start。高优先级不一定先调用。

public class TestPriority{
    
    
    public static void main(String[] args) {
    
    
        System.out.println(Thread.currentThread().getName()+ "->" +Thread.currentThread().getPriority());

        MyPriority myPriority = new MyPriority();
        Thread thread1 = new Thread(myPriority);
        Thread thread2 = new Thread(myPriority);
        Thread thread3 = new Thread(myPriority);
        Thread thread4 = new Thread(myPriority);
        Thread thread5 = new Thread(myPriority);
        Thread thread6 = new Thread(myPriority);

        thread1.start();

        thread2.setPriority(1);
        thread2.start();

        thread3.setPriority(4);
        thread3.start();

        thread4.setPriority(Thread.MAX_PRIORITY);
        thread4.start();
        


    }
}

class MyPriority implements Runnable{
    
    

    @Override
    public void run() {
    
    
        System.out.println(Thread.currentThread().getName()+ "->" +Thread.currentThread().getPriority());
    }
}

守护线程

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
public class TestDaemon {
    
    
    public static void main(String[] args) {
    
    
        God god = new God();
        You1 you1 =new You1();

        Thread thread1 = new Thread(god);
        thread1.setDaemon(true);//默认false,代表用户线程
        thread1.start();        //上帝线程启动

        new Thread(you1).start();//你线程启动
    }
}

//上帝
class God implements Runnable{
    
    

    @Override
    public void run() {
    
    
        while(true){
    
    
            System.out.println("上帝线程守护你线程");
        }

    }
}

//你
class You1 implements Runnable{
    
    

    @Override
    public void run() {
    
    
        for (int i = 0; i < 1000; i++) {
    
    
            System.out.println("你线程执行");
        }
        System.out.println("你线程结束");
    }
}

线程同步(线程安全)

线程同步:出现在多个线程操作同一个资源。是一种等待机制。

线程同步:sychronized关键字。

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

并发问题:

//多个线程同时操作同一个对象
//买火车票
public class TestThread4 implements Runnable{
    
    
    //票数
    private int ticketNums = 10;
    @Override
    public void run() {
    
    
        while (ticketNums > 0) {
    
    
            try {
    
    
                //模拟延时
                Thread.sleep(500);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "线程1拿票第" + ticketNums-- + "票");
        }
    }

    public static void main(String[] args) {
    
    
        TestThread4 ticket = new TestThread4();

        new Thread(ticket,"小明").start();
        new Thread(ticket,"小花").start();
        new Thread(ticket,"小红").start();

    }
}

执行结果:

小红线程1拿票第10票
小花线程1拿票第9票
小明线程1拿票第10票
小红线程1拿票第8票
小花线程1拿票第8票
小明线程1拿票第7票
小花线程1拿票第6票
小明线程1拿票第4票
小红线程1拿票第5票
小红线程1拿票第3票
小花线程1拿票第1票
小明线程1拿票第2票

sychronized

sychronized使用:

  • sychronized方法

    在方法用sychronized修饰

    public sychronized void buy(){
          
          
    }
    //sychronized相当于一把锁,它把这个方法对应的对象锁着,一次只能一个人访问该对象。
    //对于上述买车票问题线程同步实现
    public sychronized void run() {
          
          
            while (ticketNums > 0) {
          
          
                try {
          
          
                    //模拟延时
                    Thread.sleep(500);
                } catch (InterruptedException e) {
          
          
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "线程1拿票第" + ticketNums-- + "票");
            }
     }
    //该锁锁的是TestThread4这个对象
    
  • sychronized块

    sychronized(obj){
          
          
    	实现;
    }
    //块锁的是该obj对象,一次只能一个人访问该对象,而不是方法对应的对象。
    //将上述买票重新写一下,用锁块实现同步,这个锁的是ticket这个对象
    
    class Ticket{
          
          
        public int ticketNums = 20;
    }
    
    public class UnsafeBuyTicket implements Runnable{
          
          
        //票数
        private int ticketNums = 10;
        boolean flag = true;
        Ticket ticket = new Ticket();
    
        @Override
        public void run() {
          
          
            //买票
            while(flag){
          
          
                try {
          
          
                    buy();
                } catch (InterruptedException e) {
          
          
                    e.printStackTrace();
                }
            }
        }
    
        public void buy() throws InterruptedException {
          
          
    
            synchronized (ticket){
          
          
                if(ticket.ticketNums <= 0){
          
          
                    this.flag = false;
                    return;
                }
                Thread.sleep(100);
                //买票
                System.out.println(Thread.currentThread().getName() + "买到第" + ticket.ticketNums-- +"张票");
            }
    
        }
    
        public static void main(String[] args) {
          
          
            UnsafeBuyTicket unsafeBuyTicket = new UnsafeBuyTicket();
    
            new Thread(unsafeBuyTicket,"小明").start();
            new Thread(unsafeBuyTicket,"小华").start();
            new Thread(unsafeBuyTicket,"小红").start();
    
        }
    }
    

死锁

死锁:都在相互等待对方的资源。

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

  • 互斥条件:一个支援每次只能被一个进程使用。
  • 请求和保持条件:一个进程因请求资源而堵塞时,对方获得的资源褒词不放。
  • 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

避免程序中出现死锁。

锁(Lock)

Lock是接口,常用实现类ReentrantLock(可重复锁)。

import java.util.concurrent.locks.ReentrantLock;

//测试lock锁
public class TestLock {
    
    
    public static void main(String[] args) {
    
    
        TestLock1 testLock11 = new TestLock1();
        new Thread(testLock11).start();
        new Thread(testLock11).start();
        new Thread(testLock11).start();
    }
}

class TestLock1 implements Runnable{
    
    

    int ticket = 10;
    ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
    
    
        while (true){
    
    
            try {
    
    
                lock.lock();
                if(ticket >0){
    
    
                    try {
    
    
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    System.out.println(ticket--);
                }else {
    
    
                    break;
                }
            }finally {
    
    
                lock.unlock();
            }

        }
    }
}

线程通信

生产者消费者 问题

管程法

//生产者消费者模型,管程法
//生产者,消费者,产品,缓冲区
public class TestPC {
    
    
    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;
    }
    ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
    
    
       produce();
    }

    void produce() {
    
    
        for (int i = 0; i < 30; i++) {
    
    
                container.push(new Thing(i+1));
                System.out.println("生产了第"+ (i+1) +"个产品");

        }

    }
}

//消费者
class Consumer extends Thread{
    
    
    SynContainer container;
    public Consumer(SynContainer container){
    
    
        this.container = container;
    }

    @Override
    public void run() {
    
    
        for (int i = 0; i < 30 ; i++) {
    
    
            System.out.println("消费了第"+ container.pop().id +"个产品");
        }
    }
}

//产品
class Thing{
    
    
    int id;//产品编号

    public Thing(int id) {
    
    
        this.id = id;
    }
}

//缓冲区
class SynContainer{
    
    
    Thing[] things = new Thing[10];
    int count = 0;
    //生产者放入产品
    public synchronized void push(Thing thing){
    
    
        //容器满,等待消费者取
        if(count == things.length){
    
    
            //等待消费者取
            try{
    
    
                this.wait();
            }catch (InterruptedException e){
    
    
                e.printStackTrace();
            }
        }
        //容器未满,放产品
        things[count++] = thing;

        this.notifyAll();

    }
    //消费者取出产品
    public synchronized Thing pop(){
    
    
        //判断是否能消费
        if(count == 0){
    
    
            //等待生产者放产品
            try {
    
    
                this.wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }

        //如果可以消费
        count--;
        Thing thing = things[count];

        this.notifyAll();
        return thing;
    }
}

线程池

//ExecutorService 线程池接口
//Executors 线程池工具类
//创建线程池,参数位线程池中的线程个数
ExecutorService ser = Executors.newFixedThreadPool(2)
//Runnable接口用execute()无返回值
ser.execute(new RunnableImpl)
//Callable接口用submit()有返回值
Future<V> result = ser.submit(new CallableImpl);

猜你喜欢

转载自blog.csdn.net/weixin_47063773/article/details/109212391