Java多线程篇

一.进程与线程的关系

1.进程(程序跑起来执行的过程):可以理解为完成一个任务,是由系统分配的。
2.线程:去执行任务的人。
总结:一个进程可以包含多个线程,但至少包含一个线程,不然进程没有存在的意义。

二.核心概念

1.线程状态:new新生》start就绪》线程调度器调用运行》消亡或阻塞
2.当程序运行时即使没有主动创建线程:后台也会有多个线程,如程序的入口main()主线程,gc线程。
3.调度器:一个进程如果开辟了多个线程,线程的运行是由调度器来安排的,且不能干预。
4.优先权:每个线程都有优先权
5.守护线程

三.创建线程的三种方式

1.继承Thread类:重写run方法,创建线程对象,调用start方法

public class TestThread1 extends Thread{
	
	//重写run方法:线程执行的任务
    @Override
    public void run(){
        for (int i=1; i<=2000; i++){
            System.out.println("自定义线程"+i);
        }
    }
    
	//创建线程对象,调用start方法:启动线程
    public static void main(String[] args) {
        new TestThread1().start();
    }

}

2.实现Runnable接口:重写run方法,创建线程对象后,通过代理对象Thread,调用start方法

public class TestThread2 implements Runnable{

    @Override
    public void run(){
        for (int i=1; i<=2000; i++){
            System.out.println("自定义线程"+i);
        }
    }
	
	//Thread:代理对象
    public static void main(String[] args) {
        new Thread(new TestThread2()).start();
    }

}

3.实现Callable接口并设置返回值类型:重写call方法并抛出异常,创建线程池,提交执行,获取结果并抛出异常,关闭连接池

public class TestThread3 implements Callable<Boolean> {

    @Override
    public Boolean call() throws Exception {
        for (int i=1; i<=2000; i++){
            System.out.println("自定义线程"+i);
        }
        return true;
    }

    @SneakyThrows
    public static void main(String[] args) {
     	//创建线程对象
        TestThread3 testThread3 =new TestThread3();
        //创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        //提交执行
        Future<Boolean> submit =executorService.submit(testThread3);
        //获取结果
        boolean res = submit.get();
        //关闭连接池
        executorService.shutdownNow();
    }

}

四.线程停止

1.使用标志位,定义一个标志位后利用循环条件,通过修改标志位使线程正常停止
2.不要使用stop或destroy等,jdk不建议的过时方法

public class TestStop 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) {
        TestStop testStop =new TestStop();
        new Thread(testStop).start();
        for (int i=0;i<1000;i++){
            System.out.println("主线程循环次数:"+i);
            //主线程循环900次时,停止死循环线程
            if (i==900){
                testStop.stop();
                System.out.println("死循环线程停止了");
            }
        }
    }

}

五.线程休眠

public class TestSleep {

    public static void main(String[] args) {
        down();
    }

    //模拟倒计时
    @SneakyThrows
    public static void down(){
        int num =10;
        while (true){
        	//线程睡眠时间以毫秒为单位
            Thread.sleep(1000);
            System.out.println(num--);
            if (num<=0){
                break;
            }
        }
    }

六.线程礼让

原理:让线程由运行状态转为就绪状态,礼让不一定成功,看cpu线程调度器安排

public class TestYield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始");
        //线程礼让
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程停止");
    }

    public static void main(String[] args) {
        TestYield testYield =new TestYield();
        new Thread(testYield,"a").start();
        new Thread(testYield,"b").start();
    }

}

七.线程插队

线程插队:强制执行此线程,其它线程等待,待此线程执行完成后再执行

ublic class TestJoin implements Runnable{

    @Override
    public void run() {
        for (int i=0; i <999; i++) {
            System.out.println("自定义线程插队来了"+i);
        }
    }

    @SneakyThrows
    public static void main(String[] args) {
        TestJoin testJoin =new TestJoin();
        Thread thread =new Thread(testJoin);
        thread.start();
        for (int i=0; i <300; i++) {
            //主线程循环200次时,让自定义线程插队
            if (i==201){
                thread.join();
            }
            System.out.println("主线程"+i);
        }
    }
    
}

八.线程状态

public class TestState {

    @SneakyThrows
    public static void main(String[] args) {
        Thread thread =new Thread(() ->{
            for (int i = 0; i < 3; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        //获取线程状态
        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(1000);
            state=thread.getState();
            System.out.println("阻塞状态-就绪状态-消亡状态:"+state);
        }
    }

}

九.线程优先级

原理:线程调度器会根据优先级来“权重”先调度那个线程,优先级最大10-1最小,默认5

public class TestPriority implements Runnable{

    @Override
    public void run() {
        System.out.println("名字"+Thread.currentThread().getName()+"优先级"+Thread.currentThread().getPriority());
    }

    public static void main(String[] args) {
        TestPriority testPriority =new TestPriority();
        Thread t1 =new Thread(testPriority);
        Thread t2 =new Thread(testPriority);
        Thread t3 =new Thread(testPriority);
        //默认优先级:NORM_PRIORITY=5
        t1.start();
        //最大优先级:MAX_PRIORITY=10
        t2.setPriority(Thread.MAX_PRIORITY);
        t2.start();
        //最小优先级:MIN_PRIORITY=1
        t3.setPriority(Thread.MIN_PRIORITY);
        t3.start();
    }
    
}

十.守护线程

要领:守护线程需在启动前设置,否则无效
案例:主线程先消亡情况,但只要有一个用户线程未消亡,守护线程就会存活。

public class TestDaemon {
    public static void main(String[] args) {
        Thread thread =new Thread(new Guard());
        thread.setDaemon(true);
        thread.start();

        new Thread(new User()).start();

        for (int i = 0; i < 50; i++) {
            System.out.println("主线程心跳");
        }
    }
}
//守护线程
class Guard implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("守护线程心跳");
        }
    }
}
//用户线程
class User implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("用户线程心跳");
        }
    }
}

十一.多线程不安全案例

我们都知道ArrayList线程不安全:那就用多线程去操作ArrayList看看结果

public class TestArrayList {

    @SneakyThrows
    public static void main(String[] args) {
        List<String> list =new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(2000);
        //由于线程不安全:集合长度可能是10000,9999、9998、9997....
        System.out.println("集合长度:"+list.size());
    }
}

十二.同步方法与同步块

同步锁:多线程访问同一个资源,加锁后会排队访问,同一时间内只有一个线程可以进行操作。

public class TestArrayList implements Runnable{
    
    /**
     * synchronized修饰的方法叫同步方法,作用域this,锁的是当前方法归属类的对象
     * 所以它锁的是TestArrayList类,而不是list集合
     */
    @SneakyThrows
    @Override
    public synchronized void run() {
        List<String> list =new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(2000);
        System.out.println("集合长度:"+list.size());
    }

    public static void main(String[] args) {
        new Thread(new TestArrayList()).start();
    }

}
public class TestArrayList implements Runnable{

    @SneakyThrows
    @Override
    public void run() {//任务:创建一万个线程,往list里插入一万条数据
        List<String> list =new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                //同步块:可以锁任何变化的量,使用场景增删改
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        Thread.sleep(2000);
        System.out.println("集合长度:"+list.size());
    }

    public static void main(String[] args) {
        new Thread(new TestArrayList()).start();
    }

}

十三.死锁

死锁发生条件
1.多线程互相持有对方的锁,
2.资源只有一份出现僵持,需等待资源后才能运行,
此时会出现线程停止的死锁问题。

package com.baijun.utils.entity;

import lombok.SneakyThrows;

/**
 * 死锁:多线程互相持有对方的锁,且资源只有一份出现僵持需要等待拿到对方的资源后才能运行,从而出现线程停止的死锁问题。
 */
public class TestLock extends Thread{

    //资源只有一份
    static Lipstick lipstick =new Lipstick();
    static Mirror mirror =new Mirror();

    //选择
    int choice;
    //使用化妆的人
    String girlName;

    TestLock(int choice,String girlName){
        this.choice=choice;
        this.girlName=girlName;
    }
    @Override
    public void run(){
        //化妆
        makeup();
    }
    //化妆,互相持有对方的锁,需要拿到对方的资源
    @SneakyThrows
    private void makeup(){
        if (choice==0){
            synchronized (lipstick){//口红锁
                System.out.println(girlName+"口红锁");
                Thread.sleep(1000);
                synchronized (mirror){//镜子锁
                    System.out.println(girlName+"镜子锁");
                }
            }
        }else {
            synchronized (mirror){//镜子锁
                System.out.println(girlName+"镜子锁");
                Thread.sleep(2000);
                synchronized (lipstick){//口红锁
                    System.out.println(girlName+"口红锁");
                }
            }
        }
    }

    public static void main(String[] args) {
        TestLock testLock1 =new TestLock(0,"宝子");
        TestLock testLock2 =new TestLock(1,"小凤");
        testLock1.start();
        testLock2.start();
    }

}

//口红
class Lipstick{}

//镜子
class Mirror{}

十四.lock锁

package com.baijun.utils.entity;

import lombok.SneakyThrows;

import java.util.concurrent.locks.ReentrantLock;

/**
 * Lock:是显式锁需要手动开关,synchronized是隐式锁,出了作用域自动释放。
 * 优先顺序:Lock、同步代码块、同步方法
 */
public class TestLock2 {

    public static void main(String[] args) {
        TestLock3 testLock3 =new TestLock3();
        for (int i = 0; i < 3; i++) {
            new Thread(testLock3).start();
        }
    }
}

class TestLock3 implements Runnable{

    int ticketNums =10;//    票数

    //定于Lock锁:ReentrantLock可重入锁
   private final ReentrantLock lock =new ReentrantLock();

    @SneakyThrows
    @Override
    public void run() {//买票
        while (true){
            try {
                lock.lock();//加锁
                if (ticketNums>0){
                    Thread.sleep(1000);
                    System.out.println(ticketNums--);
                }else {
                    break;
                }
            }finally {
                lock.unlock();
            }
        }
    }

}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

柏君~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值