Java多线程学习笔记

目录
  • 多任务、多线程
  • 程序、进程、线程
  • 学着看jdk文档
  • 线程的创建
    • 1.继承Thread类
    • 2.实现Runable接口
    • 理解并发的场景
    • 龟兔赛跑场景
    • 实现callable接口
  • 理解函数式接口
  • 理解线程的状态
    • 线程停止
    • 线程休眠sleep
      • 1.网路延迟
      • 2.倒计时等
    • 线程礼让yield
    • 线程强制执行
  • 观察线程状态
  • 线程的优先级
  • 守护线程
  • 线程同步机制
    • 1.synchronized 同步方法
    • 2.同步块synchronized(Obj){}
  • lock
  • synchronized与lock

多任务、多线程

在多任务场景下,两件事看上去同时在做,但实际上,你的大脑在同一时间只做一件事,间隔时间可能很少,但这似乎让你感觉这两件事是同时在做

考虑阻塞问题,引入多线程的场景,多线程并发场景

程序、进程、线程

程序=指令+数据(静态的)
在操作系统中运行的程序就是进程,一个进程可以有多个线程
比如,看视频时听声音,看图像,看弹幕等

学着看jdk文档

比如你要看Thread
你可以搜索,然后阅读

往下翻你会看到:

线程的创建

1.继承Thread类

//创建线程方式一:继承Thread类,重写run方法,调用start()方法开启线程
public class TestThread1  extends Thread{

    @Override
    public void run() {
        //run()方法线程体
        IntStream.range(0,20).forEach(i->{
            System.out.println("我在看代码"+i);
        });
    }

    public static void main(String[] args) {
        //创建一个线程对象
        TestThread1 testThread1=new TestThread1();
        //调用start()方法,启动线程,不一定立即执行,由cpu调度执行
        testThread1.start();

        //主方法 main方法
        IntStream.range(0,20).forEach(i->{
            System.out.println("我在学习多线程"+i);
        });
    }
}

一个小练习:

//练习thread实现对线程同步下载图片
public class TestThread2 extends Thread{

    private String url;
    private String name;

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

    @Override
    public void run() {
          WebDownload webDownload=new WebDownload();
          webDownload.downloader(url,name);
          System.out.println("下载了文件名:"+name);
    }

    public static void main(String[] args) {
        TestThread2 t1=new TestThread2("https://profile.csdnimg.cn/B/D/2/3_sxh06","1.jpg");
        TestThread2 t2=new TestThread2("https://profile.csdnimg.cn/B/D/2/3_sxh06","2.jpg");
        TestThread2 t3=new TestThread2("https://profile.csdnimg.cn/B/D/2/3_sxh06","3.jpg");

        t1.start();
        t2.start();
        t3.start();

    }
}

//下载器
class WebDownload{
    //下载方法
    public void downloader(String url,String name)  {
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出错");
        }
    }
}

2.实现Runable接口

//创建线程的方法2:实现Runable接口
public class TestThread3 implements Runnable{

    @Override
    public void run() {
        //run()方法线程体
        IntStream.range(0,20).forEach(i->{
            System.out.println("我在看代码"+i);
        });
    }

    public static void main(String[] args) {
        //创建一个线程对象
        TestThread3 testThread3=new TestThread3();
        //调用start()方法,启动线程,不一定立即执行,由cpu调度执行
//        Thread thread=new Thread(testThread3);
//        thread.start();

        //或者这样简写
       new Thread(testThread3).start();
        //主方法 main方法
        IntStream.range(0,100).forEach(i->{
            System.out.println("我在学习多线程"+i);
        });
    }
}

理解并发的场景

当多个线程使用同一个资源时,会出现问题,看看下面这个买火车票的例子:

public class TestThread4 implements  Runnable{

    //票数
    private int ticketNums=10;

    @Override
    public void run() {
        while(true){
            if (ticketNums<=0){
                break;
            }
            //模拟延迟
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"张票");
        }

    }

    public static void main(String[] args) {
        TestThread4 ticket=new TestThread4();

        new Thread(ticket,"小明").start();
        new Thread(ticket,"张三").start();
        new Thread(ticket,"李四").start();
    }
}

看看运行的结果:

可以看到案例中的线程不安全问题,同时数据也是不正确的

龟兔赛跑场景

/**
 * 模拟龟兔赛跑
 */
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(1);
                } 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);
            return true;
        }else{
            return false;
        }
    }

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

实现callable接口

//线程创建方式3
public class TestCallable implements Callable<Boolean> {
    private String url;
    private String name;

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

    @Override
    public Boolean call() {
        com.sxh.thread.WebDownload webDownload=new com.sxh.thread.WebDownload();
        webDownload.downloader(url,name);
        System.out.println("下载了文件名:"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1=new TestCallable("https://profile.csdnimg.cn/B/D/2/3_sxh06","1.jpg");
        TestCallable t2=new TestCallable("https://profile.csdnimg.cn/B/D/2/3_sxh06","2.jpg");
        TestCallable t3=new TestCallable("https://profile.csdnimg.cn/B/D/2/3_sxh06","3.jpg");

        //创建执行服务
        ExecutorService ser= Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1=ser.submit(t1);
        Future<Boolean> r2=ser.submit(t2);
        Future<Boolean> r3=ser.submit(t3);
        //获取结果
        boolean rs1=r1.get();
        boolean rs2=r2.get();
        boolean rs3=r3.get();
        //关闭服务
        ser.shutdownNow();
    }

}

理解函数式接口

任何接口,只包含唯一一个抽象方法,就是函数式接口

/**
 * lambdab表达式的发展
 */
public class TestLambda1 {
    //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();

        like=new Like2();
        like.lambda();

        //4.局部内部类
       class Like3 implements ILike{
            @Override
            public void lambda() {
                System.out.println("i like lambda3");
            }
        }
        like=new Like3();
        like.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");
    }
}

理解线程的状态

线程停止

public class TestStop implements Runnable{

    //1.设置一个标志位
    private boolean flag=true;
    @Override
    public void run() {
       int i=0;
       while (flag){
           System.out.println("run...thread.."+i++);
       }
    }

    //2.设置一个公开的方法停止线程,转换标志位
    public void stop(){
           this.flag=false;
    }

    public static void main(String[] args) {
        TestStop stop=new TestStop();
        new Thread(stop).start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("main"+i);
            if (i==900){
                //调用stop方法,让线程停止
                stop.stop();
                System.out.println("线程该停止了");
            }
        }
//        IntStream.range(0,1000).forEach(i->{
//
//        });
    }
}

线程休眠sleep

每个对象都有一把锁,sleep不会释放锁

1.网路延迟

            //模拟延迟
            try {
                Thread.sleep(200); //ms
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

2.倒计时等

 public static void main(String[] args) {
       try {
            tendown();
        } catch (InterruptedException e) {
            e.printStackTrace();
       }
  }
    public static void tendown() throws InterruptedException {
        int num=10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num--);
            if(num<=0)
            {
                break;
            }
        }
    }
 public static void main(String[] args) {
        //打印系统当前时间
        Date startTime=new Date(System.currentTimeMillis());
        while (true){
            try {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                startTime=new Date(System.currentTimeMillis());//更新时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

线程礼让yield

//线程礼让  礼让不一定成功,由cpu重新调度
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()+"线程停止执行");
    }
}

线程强制执行

//测试join方法  想象为插队
public class TestJoin implements  Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程vip来了"+i);
        }
    }

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

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

观察线程状态

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);   //Run
        }
    }
}

线程的优先级

//测试线程的优先级
public class TestPriority {
    public static void main(String[] args) {
        //主线程默认优先级
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());

        MyPriority myPriority=new MyPriority();

        Thread t1=new Thread(myPriority);
        Thread t2=new Thread(myPriority);
        Thread t3=new Thread(myPriority);
        Thread t4=new Thread(myPriority);
        Thread t5=new Thread(myPriority);
        Thread t6=new Thread(myPriority);
        //先设置优先级,在启动
        t1.start();
        t2.setPriority(1);
        t2.start();
        t3.setPriority(4);
        t3.start();
        t4.setPriority(Thread.MAX_PRIORITY);
        t4.start();
        t5.setPriority(-1);
        t5.start();
        t6.setPriority(11);
        t6.start();

    }
}
class MyPriority implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
    }
}

守护线程

线程分为用户线程和守护线程

//测试守护线程
public class TestDaemon {
    public static void main(String[] args) {
        God god=new God();
        You you=new You();
        Thread thread=new Thread(god);
        thread.setDaemon(true); //默认是false表示用户线程
        thread.start();

        new Thread(you).start();

    }
}

class God implements  Runnable{

    @Override
    public void run() {
      while (true){
          System.out.println("上帝保佑着你");
      }
    }
}
class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 36000; i++) {
            System.out.println("你活着"+i);
        }
        System.out.println("goodbye!!");
    }
}

线程同步机制

解决安全性问题:队列+锁

1.synchronized 同步方法

默认锁的是this,如需锁其他的,使用下面的同步块

//synchronized 同步方法
    private  synchronized void buy(){
        if (ticketNums<=0){
            flag=false;
            return;
        }
        //模拟延迟
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //买票
        System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"张票");
    }

2.同步块synchronized(Obj){}

锁的对象是变化的量,需要增删改的对象
obj称之为同步监视器,即监视对象

public class UnsafeList {
    public static void main(String[] args) {
        List<String> list=new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

lock

class A{
     //ReentrantLock 可重入锁
     private final ReentrantLock lock=new ReentrantLock();
     public void f(){
       lock.lock();//加锁
       try{
           //.....
        }
       finally{
          lock.unlock();//释放锁
        }
     }

}

synchronized与lock

  1. lock是显示锁需要手动开关,synchronized是隐式锁,出了作用域自动释放
  2. lock只有代码块锁,synchronized有代码块锁和方法锁
  3. JVM将花费更少的时间来调度线程,性能更好,更有扩展性
  4. 优先使用:Lock>同步代码块>同步方法

到此这篇关于Java多线程学习笔记的文章就介绍到这了,更多相关Java 多线程内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 一文彻底搞懂java多线程和线程池

    目录 什么是线程 一. Java实现线程的三种方式 1.1.继承Thread类 1.2.实现Runnable接口,并覆写run方法 二. Callable接口 2.1 Callable接口 2.2 Future接口 2.3 Future实现类是FutureTask. 三. Java线程池 3.1.背景 3.2.作用 3.3.应用范围 四. Java 线程池框架Executor 4.1.类图: 4.2 核心类ThreadPoolExecutor: 4.3 ThreadPoolExecutor逻辑结

  • Java多线程之并发编程的核心AQS详解

    目录 一.AQS简介 1.1.AOS概念 1.2.AQS的核心思想 1.3.AQS是自旋锁 1.4.AQS支持两种资源分享的方式 二.AQS原理 2.1.同步状态的管理 2.2.等待队列 2.3.CLH队列中的结点 2.4.队列定义 2.5.AQS底层的CAS机制 2.6.通过ReentrantLock理解AQS 三.AQS方法 3.1.用户需要自己重写的方法 3.2.AQS 提供的一系列模板方法 3.3.acquire(int)方法 3.4.release(int)方法 3.5.acquire

  • Java基础之多线程方法状态和创建方法

    目录 Java之线程的五大状态及其常用方法(六个状态还有timed_wating超时等待) 1.线程的五大状态及其转换 2.设置或获取多线程的线程名称的方法 3.线程休眠------sleep()方法 4.线程让步------yield()方法 5. 等待线程终止------join()方法 6. 线程停止 7. 线程等待------wait()方法 8. 线程唤醒-------notify()方法 9. notifyAll()方法 JAVA多线程有哪几种实现方式? 1. 继承Thread类 2

  • Java多线程之线程状态详解

    目录 线程状态 停止线程 线程休眠 模拟网络延迟(放大问题的发生性) 模拟计时 线程礼让 插队(线程强制执行) 线程状态观测 线程优先级 守护线程 总结 线程状态 五个状态:新生.就绪.运行.死亡.阻塞 停止线程 不推荐使用JDK提供的stop().destroy()方法[已弃用] 推荐线程自己停止 建议用一个标志位进行终止变量,到flag=false,则终止线程运行 public class StopDemo implements Runnable { // 设置一个标志位 boolean f

  • Java多线程基本概念以及避坑指南

    目录 前言 1. 多线程基本概念 1.1 轻量级进程 1.2 JMM 1.3 Java中常见的线程同步方式 2. 避坑指南 2.1. 线程池打爆机器 2.2. 锁要关闭 2.3. wait要包两层 2.4. 不要覆盖锁对象 2.5. 处理循环中的异常 2.6. HashMap正确用法 2.7. 线程安全的保护范围 2.8. volatile作用有限 2.9. 日期处理要小心 2.10. 不要在构造函数中启动线程 End 前言 多核的机器,现在已经非常常见了.即使是一块手机,也都配备了强劲的多核处

  • Java多线程学习笔记

    目录 多任务.多线程 程序.进程.线程 学着看jdk文档 线程的创建 1.继承Thread类 2.实现Runable接口 理解并发的场景 龟兔赛跑场景 实现callable接口 理解函数式接口 理解线程的状态 线程停止 线程休眠sleep 1.网路延迟 2.倒计时等 线程礼让yield 线程强制执行 观察线程状态 线程的优先级 守护线程 线程同步机制 1.synchronized 同步方法 2.同步块synchronized(Obj){} lock synchronized与lock 多任务.多

  • java多线程学习笔记之自定义线程池

    当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的. public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueu

  • java多线程学习之死锁的模拟和避免(实例讲解)

    1.死锁 死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放.由于线程被无限期地阻塞,因此程序不可能正常终止. Java 死锁产生的四个必要条件: 1.互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用 2.不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放. 3.请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有. 4.循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的

  • Java基础学习笔记之数组详解

    本文实例讲述了Java基础学习笔记之数组.分享给大家供大家参考,具体如下: 数组的定义于使用 1:数组的基本概念 一组相关变量的集合:在Java里面将数组定义为引用数据类型,所以数组的使用一定要牵扯到内存分配:想到了用new 关键字来处理. 2:数组的定义格式 区别: 动态初始化后数组中的每一个元素的内容都是其对应数据类型的默认值,随后可以通过下标进行数组内容的修改: 如果希望数组定义的时候就可以提供内容,则采用静态初始化的方式: a:数组的动态初始化(声明并初始化数组): 数据类型 数组名称

  • Java 注解学习笔记

    注解说明 Java注解又称Java标注,是Java语言5.0版本开始支持加入源代码的特殊语法元数据.为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据.Java语言中的类.方法.变量.参数和包等都可以被标注.和Javadoc不同,Java标注可以通过反射获取注解内容.在编译器生成类文件时,注解可以被嵌入到字节码中.Java虚拟机可以保留注解内容,在运行时可以获取到注解内容. 内置注解 Java定义了一套注解,共有7个,3个在java.lang中,剩下4个

  • Java 多线程学习详细总结

    目录(?)[-] 一扩展javalangThread类 二实现javalangRunnable接口 三Thread和Runnable的区别 四线程状态转换 五线程调度 六常用函数说明 使用方式 为什么要用join方法 七常见线程名词解释 八线程同步 九线程数据传递 本文主要讲了java中多线程的使用方法.线程同步.线程数据传递.线程状态及相应的一些线程函数用法.概述等. 首先讲一下进程和线程的区别: 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1

  • java struts2学习笔记之线程安全

    在说struts2的线程安全之前,先说一下,什么是线程安全?这是一个网友讲的. 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码.如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的. 就是说,在一个进程中有多个线程并发执行,每个线程执行过程中,变量值是相同的,执行结果也是相同的,就是线程安全的.否则就是线程不安全的. 然后回顾一下servlet的线程安全问题,由于servlet是单例模式的,只会产生一个实例,当多个用户同

  • java正则表达式学习笔记之命名捕获

    很多正则引擎都支持命名分组,java是在java7中才引入这个特性,语法与.Net类似(.Net允许同一表达式出现名字相同的分组,java不允许). 命名分组很好理解,就是给分组进行命名.下面简单演示一下java中如何使用以及注意事项. 1.正则中定义名为NAME的分组 (?<NAME>X) 这里X为我们要匹配的内容,注意,在这个命名不能重复,名字也不能以数字开头! 2.反向引用NAME组所匹配到的内容 \k<NAME> 注意,反向引用是针对组所匹配到的内容,而非组的表达式. 3.

  • java虚拟机学习笔记进阶篇

    上一节是把大概的流程给过了一遍,但是还有很多地方没有说到,后续的慢慢会涉及到,敬请期待! 这次我们说说垃圾收集器,又名gc,顾名思义,就是收集垃圾的容器,那什么是垃圾呢?在我们这里指的就是堆中那些没人要的对象. 1.垃圾收集器的由来 为什么要有垃圾收集器啊?不知道有没有想过这个问题,你说我运行一个程序要什么垃圾收集器啊? 随意看一下下面两行代码: User user = new User("root","123456") user = new User("

  • java虚拟机学习笔记基础篇

    1.前言(基于JDK1.7) 最近想把一些java基础的东西整理一下,但是又不知道从哪里开始!想了好久,还是从最基本的jvm开始吧!这一节就简单过一遍基础知识,后面慢慢深入... 水平有限,我自己也是很难把jvm将清楚的,我参考一本书<深入java虚拟机第二版>(版本比较老,其实很多大佬的博客都是参考的这本书的内容...) 所谓jvm,又名java虚拟机.我们平常写java程序的时候几乎是感觉不到jvm的存在的,我们只需要根据java规范去编写类,然后就可以运行程序了,当然只有我们程序出现bu

随机推荐