多线程基础

Posted dingwen_blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程基础相关的知识,希望对你有一定的参考价值。

文章目录

多线程基础

笔记总结自: 狂神说多线程 https://www.bilibili.com/video/BV1V4411p7EF?p=1

1. 基本概念

  • 进程:由操作系统分配的独立的内存空间(理解为一个应用的),由至少一个线程组成
  • 线程:进程中的一个执行任务,多个线程共享内存(多线程安全问题)

2. 创建线程

2.1 继承自Thread,重写run()方法

package thread;

/**
 * 创建线程方式一
 *
 * @author dingwen
 * @date 2021/08/30
 */
public class CreateThread1 extends Thread 
    @Override
    public void run() 
        // 获取当前线程名称
        System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
        System.out.println("创建了一个新的线程");
    

    public static void main(String[] args) 
        // main 线程
        System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
        CreateThread1 thread1 = new CreateThread1();
        thread1.start();
    


2.2 实现Runnable接口,重写run()

基于静态代理实现:通过构造器传入目标对象,再由代理对象执行相对应的方法进行调用。

package thread;

/**
 * 创建线程方式2
 *  静态代理方式实现
 *
 * @author dingwen
 * @date 2021/08/30
 */
public class CreateThread2 implements Runnable
    @Override
    public void run() 
        System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
        System.out.println("创建了一个新的线程");
    

    public static void main(String[] args) 
        System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
        System.out.println("main 线程");
        new Thread(new CreateThread2()).start();
    


2.3 实现Callable接口,重写call()

package thread;

import java.util.concurrent.*;

/**
 * 创建线程方式3
 *
 * @author dingwen
 * @date 2021/08/30
 */
public class CreateThread3 implements Callable<Boolean> 
    @Override
    public Boolean call() throws Exception 
        System.out.println("新的线程创建成功");
        return true;
    

    public static void main(String[] args)

        try 
            System.out.println("main 线程");
            System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
            CreateThread3 thread3 = new CreateThread3();
            // 创建执行线程服务
            // 指定线程池大小为1
            ExecutorService executorService = Executors.newFixedThreadPool(1);
            // 提交执行
            Future<Boolean> submit = executorService.submit(thread3);
            System.out.println("submit.get() = " + submit.get());
            // 关闭
            executorService.shutdownNow();
         catch (InterruptedException e) 
            e.printStackTrace();
         catch (ExecutionException e) 
            e.printStackTrace();
        

    

3.案例

3.1 多线程

3.1.1 继承Thread实现

package thread;

/**
 * demo01
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class Demo01 extends Thread
    @Override
    public void run() 
        for (int i = 0; i < 100; i++) 
            System.out.println("新线程执行第:" + i + "次");
        
    

    public static void main(String[] args) 
        new Demo01().start();
        for (int i = 0; i < 2000; i++) 
            System.out.println("main线程执行第" + i + "次");
        
    

    // 结果: 新线程和main线程交叉执行


3.1.2 实现Runable接口实现

package thread;

import com.sun.imageio.plugins.jpeg.JPEGStreamMetadataFormat;

/**
 * demo02
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class Demo02 implements Runnable 
    @Override
    public void run() 
        for (int i = 0; i < 200; i++) 
            System.out.println("新启线程第" + i + "次执行");
        
    

    public static void main(String[] args) 
        new Thread(new Demo02()).start();
        for (int i = 0; i < 2000; i++) 
            System.out.println("main线程第" + i + "次执行");
        
        // 两个线程交替执行
    


3.2 多线程同步下载网络图片

package thread;

import thread.util.WebDownloaderUtil;

/**
 * demo3
 * 多线程下载网络图片
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class Demo3 extends Thread 

    /**
     * url
     */
    private String url;
    /**
     * name
     */
    private String name;

    public Demo3(String url, String name) 
        this.url = url;
        this.name = name;
    

    @Override
    public void run() 
        WebDownloaderUtil.downloader(url,name);
        System.out.println("下载了" + name + "的文件");
    

    public static void main(String[] args) 
        Demo3 t1 = new Demo3("https://www.baidu.com/link?url=mdAf6qUc5dCTIwLD6Q3jKwmvMcs6CJn5rpEliOxb1B-Pgu34Am-3JXArS5gvLzD4WQcxNpkjnUs82u4-4QeXFglMhVnXJaMQT2D5f7shBaM5P1YlSy6T7n7gDm09saMO2r18Xx874j8MCpanE7RUI9e5ayrUytDgIvMIHs0kbNfDqLN_OTOcz7R3DkdCNs17dhv5nuFNaxvZcUydmYrNI0CkfCS7gr0J18cG52ybGY7G7id_qrozY6ge0X6zVGahnipw_otkqpNj7UW9Jxu1BCtjQhFTNR5oKUBJSk9-Q0X-GaxUd6gbxSdvHttGeilkyZINSs5pHwgLNjgF_cLmeJZzkRpWz0b90mxDxJKw0A3h7a_cDpwfPNxOBSV2lWFPRbr5ELC2ghRP7-7VBx17xiIPfLWvy8064oorvY1aAtdHprwtQWhvQIjmR3d8q4y1qevgdnNGhO6f7crGeODdiMJJBgw4BicsxqoXLiIW-Eg4iPzZlQ7R1AH_sTzR0BS6pm4Cy-Jy8aqZKjAyQCjW0t7JjqN1caYoOD0N1ymSU62K2mytfhqLmMDWde-AiJ1LAb14qUachbDAYRtTA3Nei_&wd=&eqid=8cae01e9000066230000000361343a02","1.jpg");
        Demo3 t2 = new Demo3("https://image.baidu.com/search/detail?ct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=tupain&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=undefined&hd=undefined&latest=undefined&copyright=undefined&cs=67323756,518322526&os=2809338534,1082215187&simid=3123418117,3816291338&pn=25&rn=1&di=13640&ln=295&fr=&fmq=1630812709686_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=0&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%253A%252F%252Fdingyue.ws.126.net%252FMo%253DX4X4Fqvj4wcNACNAdR6VVDsPFblJ4oyKNgmcj60dR41554622336168.jpg%26refer%3Dhttp%253A%252F%252Fdingyue.ws.126.net%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1633404709%26t%3D155921677fb036dbeaba0e702e439475&rpstart=0&rpnum=0&adpicid=0&nojc=undefined","2.jpg");
        Demo3 t3 = new Demo3("https://image.baidu.com/search/detail?ct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=tupain&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=undefined&hd=1&latest=0&copyright=0&cs=2977941019,1842819723&os=2489967780,3193237028&simid=3446759049,228943532&pn=46&rn=1&di=9240&ln=295&fr=&fmq=1630812709686_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=10e&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%253A%252F%252Fimg2.liuxue360.com%252Fphoto%252F2016%252F05%252F21%252F20160521175959003.jpg%26refer%3Dhttp%253A%252F%252Fimg2.liuxue360.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1633404751%26t%3Dcf38a64223ccaf92b7617a9d5543a7eb&rpstart=0&rpnum=0&adpicid=0&nojc=undefined","3.jpg");

        t1.start();
        t2.start();
        t3.start();
        // 每次执行下载的顺序都不一样
    


3.3 买票

package thread;

public class Demo04 implements Runnable 
    /**
     * 票数
     */
    private Integer tickets = 10;

    @Override
    public void run() 
        while (true) 
            if (tickets <= 0) 
                break;
            
            try 
                // 模拟延时
                Thread.sleep(200);
             catch (InterruptedException e) 
                e.printStackTrace();
            

            System.out.println(Thread.currentThread().getName() + "拿到了第" + tickets-- + "张票");
        
    

    public static void main(String[] args) 
        Demo04 demo04 = new Demo04();
        new Thread(demo04,"tony").start();
        new Thread(demo04,"lucy").start();
        new Thread(demo04,"黄牛党").start();

        // 多个线程竞争使用统一份资源是导致第线程安全问题
    


3.4 龟兔赛跑

package thread;

/**
 * demo05
 * 龟兔赛跑
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class Demo05 implements Runnable
    /**
     * 赢家
     */
    private static String winner;
    @Override
    public void run() 
        for (int i = 0; i <= 100; i++) 
            if(judge(i))
                break;
            
           String  currentThreadName = Thread.currentThread().getName();
            System.out.println(currentThreadName+"跑了"+i+"步");

            // 模拟兔子休息
            if (currentThreadName.equals("乌龟") && i % 10 == 0)
                try 
                    Thread.sleep(200);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            

        
        
    

    /**
     * 判断游戏是否结束
     *
     * @param steps 步骤
     * @return boolean
     */
    private boolean judge(int steps)
        if (winner != null)
            return true;
        
        if (steps == 100)
            winner = Thread.currentThread().getName();
            System.out.println("赢家是:"+winner);
            return true;
        
        return false;
    

    public static void main(String[] args) 
        Demo05 demo05 = new Demo05();
        new Thread(demo05,"乌龟").start();
        new Thread(demo05,"兔子").start();
    


4.线程状态

  • 新生
  • 就绪
  • 运行
  • 阻塞
  • 死亡

5.常用方法

方法说明
setPriority(int newPriority)设置线程的优先级 范围[1,10]
static void sleep(long mills)让当前线程休眠执行的毫秒数
void join()插队
static void yield()线程礼让
void interrupt ()中断线程(不推荐使用)
boolean isAlive()线程存活状态

6.线程停止

推荐让线程自己停下来(run()执行完毕),不建议采用JDK提供的stop()、destory()等方法。建议使用一个标识位外部改变使得线程执行完毕自然停止。

7.线程休眠

sleep(毫秒数)方法使得当前线程休眠进入堵塞状态,持有对象锁且不会释放锁。

8.线程礼让

让当前正在运行的线程重新进入就绪状态等待CPU调度。

package thread;

/**
 * 线程礼让测试
 *
 * @author dingwen
 * @date 2021/09/05
 */
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,"线程1").start();
        new Thread(testYield,"线程2").start();
    


9.线程插队

join()插队,等待该线程执行完毕其他线程再执行,且其他线程进入堵塞状态。

package thread;

/**
 * join() 测试
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class TestJoin implements Runnable 
    @Override
    public void run() 
        for (int i = 0; i < 200; i++) 
            System.out.println(Thread.currentThread().getName() + i);
        
    

    public static void main(String[] args) 
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin, "new");
        thread.start();
        for (int i = 0; i < 200; i++) 
            if (i == 100) 
                try 
                    thread.join();
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
            System.out.println(Thread.currentThread().getName() + i);
        
    

10.线程状态

  • Thread.State state = new Thread().getState()
    • NEW 新生状态
    • RUNNABLE 运行状态
    • BLOCKED 阻塞状态
    • WAITING 正在等待另外一个线程执行特定动作
    • TIMED_WAITING 正在等待另一个线程执行动作达到指定等待时间的线程
    • TERMINATED 已经退出的线程

11.线程优先级([1,10])

Java提供一个线程调度器根据优先级来调度线程池中处于就绪状态的线程。

  • Thread.MAX_PRIORITY 10
  • Thread.MIN_PRIORITY 1
  • Thread.NORM_PRIORITY 5

默认值为5,在调用start()之前设置优先级,注意:优先级只是确定了CPU调度的概率。

setPriority(【优先

以上是关于多线程基础的主要内容,如果未能解决你的问题,请参考以下文章

多线程编程

java多线程实例---很有用 详细介绍

HashMap多线程不安全问题总结

java多线程远程协作(狂神说)附上课代码

java多线程实例---很有用 详细介绍

多线程系列四:AQS-AbstractQueuedSynchronizer