初步了解线程

本文详细介绍了Java中的程序、进程、线程概念,以及线程的三种创建方式(继承Thread类、实现Runnable接口和Callable接口)。重点讲解了多线程应用、线程同步、死锁、Lock与synchronized的区别以及线程池的原理和使用。
摘要由CSDN通过智能技术生成

程序、进程和线程

  • 程序:指令和数据的有序集合,一个静态的概念。ian
  • 进程:执行程序的一次执行过程,一个动态的概念。是系统资源分配的单位。
  • 线程:一个进程有若干个线程。是CPU调度和执行的单位。

线程创建三种方式

  • 继承 Thread 类
  • 实现 Runnable 接口
  • 实现Callable 接口(了解)

推荐使用第二种,实现Runnable接口,灵活方便

继承Thread类

package CreateThread.demo1;

public class ThreadDemo1 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我是线程" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

实现Runnable接口

package CreateThread.demo2;

public class ThreadDemo2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我是线程" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

实现Callable接口

TestCallable thread = new TestCallable();//实现了Callable接口的自定义类
ExecutorService ser = Executors.newFixedThreadPool(1);//创建执行服务
Future<Boolean> result = ser.submit(thread);//执行提交,Boolean这里是泛型
boolean r = result.get();//获取结果
ser.shutdownNow();//关闭服务

线程使用例子

多线程输出

package CreateThread.demo1;

// 线程不一定马上执行,由CPU调度决定
public class Main {
    public static void main(String[] args) {

        ThreadDemo1 threadDemo1 = new ThreadDemo1();
        threadDemo1.start();

        for (int i = 1; i < 20; i++) {
            System.out.println("我是主线程" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

多线程下载图片

线程类

package UserThread;

import org.apache.commons.io.FileUtils;

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

public class ThreadDownload extends Thread {
    private String url;
    private String name;

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

    @Override
    public void run() {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
            System.out.println("下载文件" + name);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("下载文件" + name + "异常");
        }

    }
}

main

package UserThread;

//多线程下载图片
public class Main {
    public static void main(String[] args) {
        ThreadDownload threadDownload1 = new ThreadDownload("https://img-blog.csdnimg.cn/img_convert/1f20816e15694cf3b56262c1f87e2a14.png","1.jpg");
        ThreadDownload threadDownload2 = new ThreadDownload("https://imgconvert.csdnimg.cn/aHR0cHM6Ly9hdmF0YXIuY3Nkbi5uZXQvNy83L0IvMV9yYWxmX2h4MTYzY29tLmpwZw","2.jpg");
        ThreadDownload threadDownload3 = new ThreadDownload("https://profile-avatar.csdnimg.cn/b2653aef86c741d6a93d3606ba678d58_weixin_43996464.jpg!1","3.jpg");

        threadDownload1.start();
        threadDownload2.start();
        threadDownload3.start();

    }
}

龟兔赛跑

线程类

package UserThread.demo3;

import UserThread.demo2.TickMain;

public class ThreadDemo implements Runnable {

    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            // 兔子会每步休息一下
            if (Thread.currentThread().getName().equals("兔兔") && i != 1) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (gameOver(i)) {
                break;
            }

            System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
        }
    }

    private boolean gameOver(int step) {
        // 已经有胜利者
        if (RaceMain.winner != null) {
            return true;
        }
        // 抵达终点
        if (step >= 10) {
            RaceMain.winner = Thread.currentThread().getName();
            System.out.println(Thread.currentThread().getName() + "到达终点!");
            return true;
        }
        // 比赛继续
        return false;
    }
}

RaceMain

package UserThread.demo3;

public class RaceMain {
    public static String winner;

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();

        new Thread(threadDemo, "兔兔").start();
        new Thread(threadDemo, "龟龟").start();
    }
}

线程并发问题

多线程抢票

多线程抢票,并发问题,会出现同一张票多个人抢到

线程类

package UserThread.demo2;

public class ThreadDemo implements Runnable {

    @Override
    public void run() {
        while (true) {
            if (TickMain.ticks <= 0) {
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "抢到票" + TickMain.ticks--);
        }
    }
}

TickMain

package UserThread.demo2;

//多线程抢票,并发问题,会出现同一张票多个人抢到
public class TickMain {
    public static int ticks = 10;

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();

        new Thread(threadDemo,"小明").start();
        new Thread(threadDemo,"小红").start();
        new Thread(threadDemo,"黄牛").start();
    }
}

线程停止

  • 不使用不推荐的停止方法,例如官方的stop、destroy()
  • 建议使用标志位来控制线程停止
package UserThread.demo4;

// 线程停止
// 不使用不推荐的停止方法,例如官方的stop、destroy()
// 建议使用标志位来控制线程停止
public class StopThread implements Runnable {

    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println("线程输出" + i++);
        }
    }

    public void stop() {
        flag = false;
    }

    public static void main(String[] args) {
        StopThread stopThread = new StopThread();

        new Thread(stopThread).start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("main" + i);
            if (i == 900) {
                stopThread.stop();
                System.out.println("线程停止-------------");
            }
        }
    }
}

线程相关方法

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

线程五个状态

  • NEW:尚未启动状态。
  • RUNNABLE:在Java虚拟机中执行的线程处于此状态。
  • BLOCKED:被阻塞等待监视器锁定的线程处于此状态。
  • WITING:正在等待另一个线程执行特定动作的线程处于此状态。
  • TIME_WITING:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
  • TERMINATED:已退出的线程。

通过getState()就可以获取线程的状态。

守护线程

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

线程同步

线程同步就是给共用资源加锁,线程需要获得锁才能进行操作,否则会阻塞。

关键字 synchronized,两种用法:synchronized 方法 和 synchronized 块。

//同步方法
pubilc synchronized void method(int args){
    ...
}

//同步块
synchronized(obj){
    ...
}

1.同步块 obj 一般都是共享资源。

2.同步方法默认锁 this。

3.若将一个大方法申明为同步,会降低效率。

生产消费者问题

管程法

package UserThread.GuanCheng;

// 多线程-管程法处理线程通信
public class GuanCheng {
    public static void main(String[] args) {
        BufferChicken bufferChicken = new BufferChicken();

        new Productor(bufferChicken).start();
        new Consumer(bufferChicken).start();
    }
}

// 生产者
class Productor extends Thread {
    private BufferChicken buffer;

    public Productor(BufferChicken buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        // 生产
        for (int i = 1; i < 100; i++) {
            buffer.push(i);
        }
    }
}

// 消费者
class Consumer extends Thread {
    private BufferChicken buffer;

    public Consumer(BufferChicken buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        // 生产
        for (int i = 1; i < 100; i++) {
            buffer.pop();
        }
    }
}

// 产品
class Chicken {
    private int id;

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

    public int getId() {
        return id;
    }
}

// 缓冲区
class BufferChicken {
    private Chicken[] chicken = new Chicken[10];
    private int count = 0;

    // 生成
    public synchronized void push(int id) {
        // 满了,等待消费
        if (count >= chicken.length) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 生产
        Chicken chicken = new Chicken(id);
        System.out.println("生产第" + id + "只鸡!");
        this.chicken[count++] = chicken;
        this.notifyAll();
    }

    //消费
    public synchronized void pop() {
        //判断是否存在产品,若不存在则等待生产
        if (count <= 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //消费
        System.out.println("消费第" + this.chicken[--count].getId() + "只鸡~");
        this.notifyAll();
    }
}

信号灯法

package UserThread.SignalLight;

// 信号灯法
public class SignalLight {
    public static void main(String[] args) {
        TV tv = new TV();
        new Productor(tv).start();
        new Comsumer(tv).start();
    }
}

//生产者
class Productor extends Thread {
    private TV tv;

    public Productor(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i % 2 == 0) {
                tv.play("斗破苍穹");
            } else {
                tv.play("斗罗大陆");
            }
        }
    }
}

//消费者
class Comsumer extends Thread {
    private TV tv;

    public Comsumer(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

//产品
class TV {
    private String name;
    // true的时候表演,false的时候观看
    private boolean flag = true;

    //表演
    public synchronized void play(String name) {
        if (!flag) {
            try {
                System.out.println("表演阻塞拉!");
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("演员表演了:" + name);
        this.name = name;
        this.flag = false;
        this.notifyAll();
    }

    //观看
    public synchronized void watch() {
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("观众观看了:" + this.name);
        this.flag = true;
        this.notifyAll();
    }

}

死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致多个线程都在等待对方释放资源,都停止执行的情况就是死锁。

某一个同步块同时拥有两个以上对象的锁时,就可能发生死锁的问题。

产生死锁的四个必要条件

  • 互斥条件:一个资源每次只能被一个进程使用。
  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
  • 循环等待条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

上面列出了4个必要条件,只需破坏任意一个条件即可避免死锁。

Lock 与 synchronized 的区别

  • Lock 是显式锁(手动开关锁),synchronized是隐式锁,出了作用域自动释放。、
  • Lock 只有代码块锁, synchronized 有代码块锁和方法锁
  • 使用 Lock 锁,JVM 将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)。
  • 优先使用顺序:lock > 同步代码块 > 同步方法

使用lock锁例子:

private final ReentrantlLock lock = new ReentrantlLock();

public void method(){
    lock.lock();
    
    ...
        
    lock.unlock();
}

线程池

什么是线程池

  • 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大
  • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用,类似生活中的公共交通工具
  • 好处:
    • 提高响应速度(减少了创建新线程的时间)
    • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
    • 便于线程管理(…)
      • corePoolSize:核心池的大小
      • maximumPoolSize:最大线程数
      • keepAliveTime:线程没有任务时最多保持多长时间后会终止

使用线程池

  • ExecutorService:线程池接口。常见子类ThreadPoolExecutor
    • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
    • Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
    • void shutdown():关闭连接池
  • Executors:工具类、线程池的工厂类,用于创建并返回不用类型的线程池

七大参数

  • corePoolSize:线程池核心线程大小
  • maximumPoolSize:线程池最大线程数量
  • keepAliveTime:空闲线程存活时间
  • unit:空闲线程存活时间单位
  • workQueue:工作队列 (1.ArrayBlocking 2.LinkedBlockingQueue 3.SynchronizedQueue 4.PriorityBlockingQueue)
  • ThreadFactory:线程工厂
  • Handler 拒绝策略
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值