java基础之多线程

一、线程、进程

1.概念

  1. 进程是程序一次执行的过程,或是正在运行中的程序,是静态的概念
  2. 线程是一个任务从头到尾的执行过程,是动态的概念
  3. 一个进程同一时间并行执行多个线程,就是支持多线程的
  4. 并行和并发:并行是多个CPU同时执行多个任务,并发是一个CPU执行多个任务

2.线程的创建

三种:线程的三种创建方式

方法一:继承Thread类

步骤:

  1. 定义子类继承Thread,使其成为线程类
  2. 子类中重写Thread类中的run方法
  3. 创建子类的对象,再调用start方法启动线程

源码分析:

package com.tang.dem01;

/**
 * @author tanglei
 * @create 2021-05-09 8:35
 * 创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
 * 线程开启不一定就立即开始执行,由CPU调度执行
 */
public class TestThread1 extends Thread{//第一步继承
    @Override
    public void run() {//第二步重写run
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("副线程---");
        }
    }

    public static void main(String[] args) {
        //主线程
        TestThread1 testThread1 = new TestThread1();
        testThread1.start();//第三步:创建对象调用start

        for (int i = 0; i < 20; i++) {
            System.out.println("主线程" + i);
        }
    }
}

方法二:实现Runnable接口

步骤:

  1. 实现Runnable接口
  2. 重写run方法,在run里面执行线程
  3. 在主函数里创建接口实现类的对象
  4. 再在主函数里面创建线程Thread对象,将接口类的实现对象传入进去
  5. 调用start方法

源码分析:

package com.tang.dem01;

/**
 * @author tanglei
 * @create 2021-05-09 9:00
 *实现接口,重写run,丢入接口实现类,调用start方法
 */
public class TestThread2 implements Runnable{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 1000; i++) {
            System.out.println("副线程---");
        }
    }

    public static void main(String[] args) {
        //创建runnable接口的实现类对象
        TestThread2 testThread2 = new TestThread2();
        //创建线程对象,通过线程对象来开启线程
        Thread thread = new Thread(testThread2);
        thread.start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("主线程" + i);
        }
    }
}

方法三:实现Callable接口

步骤:

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象(到此为止,和前面两种方法差不多)
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(n);//这里的n表示线程数目
  5. 提交执行:Future<Boolean> result1 = ser.submit(t1);
  6. 获取结果:boolean r1 = result1.get();
  7. 关闭服务:ser.shutdownNow();

3.前两种方法创建线程的区别

  • 继承Thread是将线程代码存放在Thread子类的run方法中
  • 实现Runnable是存放在接口的子类的run方法中
  • 通常采用实现Runnable的方式,这样可以避免单继承的局限性,多个线程可以共享一个接口实现类的对象,适合多个相同线程来处理同一份资源

4.龟兔赛跑实例------巩固多线程

思路:

  • 首先要一个赛道,离终点越来越近,for循环累加实现
  • 创建判断比赛是否结束方法,通过步数判定,打印出胜利者
  • 通过延迟来模拟兔子睡觉

源码分析:

package com.tang.dem01;

/**
 * @author tanglei
 * @create 2021-05-09 9:21
 */
public class Race implements Runnable{
    //胜利者
    private static String winner;
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            //模拟兔子睡觉
            if (Thread.currentThread().getName().equals("兔子") && i%10==0){
                try {
                    Thread.sleep(5);
                } 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 >= 100){
            winner = Thread.currentThread().getName();
            System.out.println("winner is "+ winner);
            return true;
        }
        return false;
    }

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

5.静态代理

方法:

  • 定义一个接口
  • 被代理类去实现该接口
  • 代理类去实现该接口,定义被代理类对象,构造函数传参
  • 在被代理类实现该接口的前后可以加入方法
  • 在主函数中创建代理类对象,再将被代理类作为参数传入代理类,调用实现接口的方法

特点:被代理(真实)对象和代理对象都要实现同一个接口,将被代理对象作为参数传入代理类。

源源码分析:

package com.tang.dem02;

/**
 * @author tanglei
 * @create 2021-05-09 9:59
 */
public class StaticProxy {
    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("要结婚了");
    }
}
//代理类
class WeddingCompany implements Marry{
    private Marry target;

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

    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();
        after();
    }

    private void after() {
        System.out.println("结婚之后");
    }

    private void before() {
        System.out.println("结婚之前");
    }
}

结果:
静态代理结果

6. Lamda表达式

函数式接口:任何接口,如果只包含了唯一一个抽象方法,那就是一函数式接口,就可以用Lamda表达式来表示。例如:

package com.tang.dem02;

/**
 * @author tanglei
 * @create 2021-05-09 10:31
 */
public class LamdaTest {
//    //3.静态内部类
//    static class Like2 implements ILike{
//        @Override
//        public void lambda() {
//            System.out.println("I lIKE lambda2");
//        }
//    }

    public static void main(String[] args) {
        ILike like = new Like();
        like.lambda();
//        Like2 like2 = new Like2();
//        like2.lambda();

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

//        //5.匿名内部类,没有类的名称,必须借助接口或者父类
//        like = new ILike() {
//            @Override
//            public void lambda() {
//                System.out.println("I lIKE lambda4");
//            }
//        };//这相当于一行语句,需要加分号
//        like.lambda();

        //6.用lambda简化
        like = ()->{
            System.out.println("I lIKE lambda5");
        };
        like.lambda();
    }
}
//1.定义一个函数式接口
interface ILike{
    void lambda();
}
//2.实现类
class Like implements ILike{
    @Override
    public void lambda() {
        System.out.println("I lIKE lambda");
    }
}
}

7. 线程方法

1.线程停止 stop:

不推荐使用stop(),destory()方法,推荐自定义标志位来停止。
停止线程

2.线程休眠 sleep:

每个线程都有一把锁,sleep不会释放锁。

3.线程礼让 yield:

让当前正在执行的线程暂停,但不阻塞,重新让CPU调度,让线程从运行状态转为就绪状态,礼让不一定成功。

package com.tang.dem02;

/**
 * @author tanglei
 * @create 2021-05-09 14:18
 */
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();

        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }
}
class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() +"线程开始执行");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName()+ "线程停止执行");
    }
}

结果:正常来说应该输出a线程执行开始到停止才到b线程执行,但是礼让之后就会可能让a,b都开始执行再停止执行。

4.线程合并 join:

待此线程执行完成后,再执行其他线程,其他线程阻塞,相当于插队。

package com.tang.dem02;

/**
 * @author tanglei
 * @create 2021-05-09 14:27
 */
public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("线程vip来了");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //启动自己的线程
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        //主线程
        for (int i = 0; i < 100; i++) {
            if (i == 20) {
                thread.join();
            }
            System.out.println("main" + i);
        }
    }
}

结果:
线程合并

8. 线程状态

线程状态转换图

package com.tang.dem02;

/**
 * @author tanglei
 * @create 2021-05-09 14:42
 */
public class TestState {
    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) {
                    e.printStackTrace();
                }
            }
            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(100);
            state = thread.getState();//更新状态
            System.out.println(state);
        }

    }
}

结果:

在这里插入图片描述

在这里插入图片描述

9. 守护线程 daemon

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕:如man();
  • 虚拟机不用等待守护线程执行完毕:如gc();

10. 线程同步

synchorized关键字可以加锁,保持同步,没有竞争才算线程安全;
调用对象上的同步实例方法需要给对象加锁,调用一个类上的同步静态方法需要给类加锁。 
死锁:某一个代码块同时拥有两个以上对象的锁,就会发生死锁。
package com.tang.dem02;

/**
 * @author tanglei
 * @create 2021-05-09 15:28
 */
public class DeadLock {
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0,"灰姑凉");
        Makeup g2 = new Makeup(1,"白雪公主");
        g1.start();
        g2.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) {
            e.printStackTrace();
        }

    }

    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
  • lock():得到一个锁,unlock释放锁,encondition()返回一个绑定到改lock实例的condition对象
  • ReentrantLock是lock的一个具体实现,该方法用于创建相互排斥的锁,可以创建具有特定的公平策略的锁。如果公平策略为真,则确保等待时间最长的线程首先获得该锁
ReentrantLock lock = new ReentranLock();
try{
lock.lock;//加锁
}catch{
...
}
finally{
lock.unlock//解锁
}

对比

11. 线程协作

通过创建lock对象的newCondition()方法而创建的对象。
await()让当前线程进入等待,直到条件发生,sinal()方法唤醒一个等待的线程,而signalAll()唤醒所有等待的线程。
协作
生产者和消费者问题。

package com.tang.dem02;

/**
 * @author tanglei
 * @create 2021-05-09 15:55
 * //利用缓冲区解决:管程法
 */
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;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Chicken(i));
            System.out.println("生产了" + i + "只鸡");
        }
    }

}
//消费者
class Consumer extends Thread{
    SynContainer container;
    public  Consumer(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) {
                e.printStackTrace();
            }


        }
        //如果容器没有满那么就需要生产产品
        chickens[count]=chicken;
        count++;
        this.notify();

    }

    //消费者消费产品
    public synchronized Chicken pop(){
        if(count==0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        count--;
        Chicken chicken = chickens[count];

        this.notifyAll();
        return chicken;
    }
}

12. 线程池

线程池

package com.tang.dem02;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author tanglei
 * @create 2021-05-09 16:21
 */
public class TestPool {
    public static void main(String[] args) {
        //1.创建线程池
        //newFixedThreadPool 参数为线程池大小
        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());
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值