java多线程

本文详细介绍了Java多线程的概念,包括程序、进程和线程的定义,以及创建线程的三种方式:继承Thread、实现Runnable和Callable接口。此外,讲解了线程的底层实现原理——静态代理模式,线程的五大状态,以及控制线程的方法如使用标志位、sleep()、yield()、join()和wait/notify。最后,讨论了Lock锁的使用及其相对于synchronized的优势。
摘要由CSDN通过智能技术生成


一:程序、进程、线程的概念

程序:指令和数据的有序集合;就是一串串有步骤的代码。就好比没有打开的王者
进程:一段程序写好了,就要运行。进程就是执行程序的一次执行过程。通俗的说,就是“上号”
线程:一个进程中至少有一个线程;当你玩王者选英雄的同时,也可以聊天;你就可以当做同时运行了选英雄线程和聊天线程;当然,有很多线程在隐藏的运行。
那多线程有什么好处呢?
你可以把写出来的程序想作一杯饮料;而进程就是装饮料的容器;多线程就相当于吸管。就是说,线程越多,程序运行越快;用户的体验感就越好。即: 高效的体验
多个线程要在CPU上运行,而CPU就一个;问题是不是就来了:你先还是我先?这里就引入了串行、并发、并行
任务调度器:CPU处理器使用时间片轮转技术,可以让多个线程切换
串行:我们排好队,一个一个的来;前一个线程没完成,后一个线程必须等着
并发:前一个线程运行时说道:等会儿,我要等待十分钟才行。CPU就说:那让后边的线程先来,你自己记住运行到哪里了就行(程序计数器
程序计数器:程序计数器记录的是该线程下一指令的地址;这里涉及了JVM原理。
并行:并发的一种理想状态 ;适用于多核CPU


二:创建线程的三种方式

1.继承Thread类

public class ThreadTest extends Thread {
    @Override
    public void run() {
    // Thread.currentThread() 表示当前执行run()的线程 | getName() 表示 获取指定线程的名称
        System.out.println("我是"+Thread.currentThread().getName());
    }
    public static void main(String[] args) {
        new Thread(new ThreadTest(),"线程一号").start();
    }
}

缺点:不利于Threadtest类的扩展;一般程序中不这么写

2.实现Runnable接口

  1. 凭什么能实现Runnable接口?
    • Thread类是实现了Runable接口的
    • Thread类有接收Runable实现类对象的构造器 public Thread(new Runnable())
  2. 直接实现Runnable接口
public class RunnableTest implements Runnable {
    @Override
    public void run() {
        System.out.println("我是"+Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        new Thread(new RunnableTest(),"线程二号").start();
    }    
}
  1. 匿名实现Runnable接口
public class RunnableTest {
    public static void main(String[] args) {
        new Thread(new Runnable(){
            public void run() {
                System.out.println("我是"+Thread.currentThread().getName());
            }
        },"线程三号").start();
    }
}

4.lamda表达式实现Runnable接口

public class RunnableTest {
    public static void main(String[] args) {
        new Thread(()->{
            System.out.println(Thread.currentThread().getName());
        },"线程三号").start();
    }
}

3.实现Callable接口(不常用)

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class CallableTest implements Callable<String> {
    @Override
    public String call() throws Exception {
        return Thread.currentThread().getName()+"is running!";
    }

    public static void main(String[] args) {
        //创建一个任务线程对象callable
        Callable<String> call = new CallableTest();
        //将任务线程对象转换为未来任务对象
        FutureTask<String> task = new FutureTask(call);
        //将未来任务对象转换为线程
        Thread newThread = new Thread(task);
        newThread.start();
        //如何获取Callable中的返回值??
        try{
            String myCallablevalue = task.get();
            System.out.println(myCallablevalue);
        }catch(Exception e){
            e.printStackTrace();						}
    }
}
  • 步骤
    1. 实现callable接口,重写call()
    2. 创建一个任务线程对象 new Callable(){ }
    3. 将任务线程对象转换为未来任务对象 new FutureTask(new Callable())
    4. 通过Thread的构造方法 public Thread(new FUtureTask())
    5. 获取Callable对象的返回值

  • 注意: JDK1.8 不支持Callable这种创建线程的写法


三、线程实现底层:静态代理模式

思考一个问题:凭什么调用Thread线程的start(),就能执行run()?
有人就要说了,一定是在start()中有run()的调用呗。那看源码:

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

这明显不是想的那样吧,连run()的影子都没有。其实,这里运用了静态代理的模式。
什么是静态代理?

  • 就好比,你是个少爷,你要吃饭;你压根不需要考虑吃饭前的做饭环节和吃饭后的洗碗环节;你就只考虑吃饭这一件事;其他的交给别人来做;这里的人就是代理的意思。话说回来,start()就只考虑启动线程;不管有没有run(),start()都要启动。可以说,start()与run()压根没关系。
public class StaticAgent {
    public static void main(String[] args) {
        You you = new You();

        Agent agent = new Agent(you);
        agent.eat();
    }

}

interface Action{
    void eat();
}
//这是你,你只考虑吃饭
/*
 * 为什么要有Action接口??
 *   仅仅是约束You的行为
 * */
class You implements Action{

    @Override
    public void eat() {
        System.out.println("我是少爷,我只考虑吃饭!");
    }
}
//我是少爷的代理
class Agent implements Action{

    private You you;

    public Agent(You you) {
        this.you = you;
    }
    @Override
    public void eat() {
        cook();
        you.eat();
        wash();
    }
    //做饭
    void cook(){
        System.out.println("我做饭 .....");
    }
    //洗碗
    void wash(){
        System.out.println("我洗碗.....");
    }
}

四、线程的五大状态

在这里插入图片描述

五、控制线程的常用方法

1.使用标志位来停止线程

public class Interrupt implements Runnable{
    @Override
    public void run() {
        System.out.println("-----");
        //判断当前线程的标志位是否为 true
        if (Thread.interrupted()){
               return;
            }
        System.out.println("-----");
    }
    public static void main(String[] args) {
        Thread thread = new Thread(new Interrupt());
        //中断该线程 中断标志位 true
        thread.interrupt();
        thread.start();
    }
}

2.线程休眠 Thread.sleep()

  • 在run()中调用sleep() 会使线程休眠,但不会释放锁对象
  • 在下列代码中,synchronized(this)在for循环之外;就算一号线程休眠,其他线程也无法进入synchronized(this)内;即:sleep()不会释放当前线程的锁对象;但在休眠时,会放弃CPU资源
public class SleepTest implements Runnable {
    private Integer number = 100;
    @Override
    public void run() {
        synchronized (this) {
        for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "---number---->" + number--);
                if (number <= 0) {
                    return;
                }
            }
        }
    }
    public static void main(String[] args) {
        SleepTest sleepTest = new SleepTest();

        new Thread(sleepTest,"一号").start();
        new Thread(sleepTest,"二号").start();
        new Thread(sleepTest,"三号").start();
    }
}

3.线程礼让 yield()

  • 理解: 线程A正在占用CPU资源,看见后面就绪的线程兄弟没抢到CPU资源。很不好意思,就说我礼让一下吧。Thread.yield();然后,线程A又变为就绪状态了。跟其他线程处在同一起跑线上。线程A说道:这一次我要是还被CPU看上,我就不礼让了哟。
public class YieldTest {

    public static void main(String[] args) {
        new Thread(new Runnable() {

            @Override
            public void run() {
                synchronized (YieldTest.class) {
                    for (int i = 0; i < 10; i++) {
                        System.out.println(Thread.currentThread().getName()+"====>"+i);
                        if (i == 5){
                            Thread.yield();
                            System.out.println(Thread.currentThread().getName()+"====>我礼让一下");
                        }
                    }
                }
            }
        },"线程A").start();
    }
}

4.线程插队join()

理解: 在线程B执行代码的时候,突然遇到这么一句:线程A.join() ;线程B就明白了,线程A要让我滚一边去。CPU给线程A运行。那好,我滚,你运行吧;等你运行好了,我再运行。

public class JoinTest {
    public static void main(String[] args) {
        Thread A = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (JoinTest.class) {
                    for (int i = 0; i < 20; i++) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "====> " + i);
                        if (i == 10){
                            try {
                                Thread.sleep(10000);
                                System.out.println(Thread.currentThread().getName() + "说:我先睡眠10秒" );
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        },"线程A");
        A.start();
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "====> " + i);
            if(i == 5){
                System.out.println(Thread.currentThread().getName() + "我接下来会运行 A.join() ;把CPU资源给线程A");
                try {
                    A.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

5.线程的等待wait()与唤醒notify() | notifyAll()

方法描述
sleep()静态方法、不释放锁对象、可以睡眠定时
wait()释放锁对象、没其他线程唤醒就会一直等待、对象锁.wait()
notify()对象锁.notify()、随机唤醒一个在该锁中被等待的线程(是谁,无所谓!能运行这个锁对象内的代码就行
notifyAll()对象锁.notifyAll()、唤醒全部在该锁中等待的线程
public class WaitAndSleep {
    public static void main(String[] args) {
        Thread A = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    WaitAndSleep.notifyClass();
                    System.out.println(Thread.currentThread().getName()+"==>唤醒");
                }
            }
        });
        Thread B = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50; i++) {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    WaitAndSleep.waitClass();
                    System.out.println(Thread.currentThread().getName()+"==>等待");
                }
            }
        });
        A.start();
        B.start();
    }
    // 同步方法 其对象锁为: WaitAndSleep.class 即 本类.class
    public static synchronized void notifyClass() {
        WaitAndSleep.class.notify();
    }
    public static void waitClass() {
        synchronized (WaitAndSleep.class) {
            try {
                WaitAndSleep.class.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

注意

  • notity()、notifyAll()、wait()都必须在同一个锁对象synchronized(锁对象) 中,不然会报异常:IllegalMonitorState

六、Lock锁

  • Lock是显示锁,手动开启关闭,synchronized(锁对象){} 是隐式锁,出了作用域会自动释放
  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好;并且具有更好的扩展性(有很多子类
  • 性能:lock锁 > 同步代码块> 同步方法
import java.util.concurrent.locks.ReentrantLock;

public class LockTest implements Runnable {

    //定义Lock锁
    private final ReentrantLock lock = new ReentrantLock();

    //定义票数
    private static Integer ticket = 10;

    //标志
    private static boolean flg = true;

    //定义buy购买方法
    public void buy() throws InterruptedException {
        if (ticket > 0){
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "买了一张票,还剩 " + ticket--);
        }else {
            flg = false;
        }
    }
    @Override
    public void run() {

        while(flg){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.lock();
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
    public static void main(String[] args) {
        LockTest test = new LockTest();

        new Thread(test,"1号柜台").start();
        new Thread(test,"2号柜台").start();
        new Thread(test,"3号柜台").start();
    }
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值