Java线程

Java线程

1、线程简介

进程和线程是两基本的单元被执行,相比较来说在并发程序中更多的是关注的JAVA线程。

Java多线程就是多个任务。

进程

一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用,在一个程序中包含多个进程。JAVA运行环境中作为一个进程包含不同的类和程序流程。

线程

线程可以被叫做轻量级的进程,线程需要更少的资源来创建和存在在这个过程中,线程共享进程的资源。 每个java应用程序有至少一个线程-(main方法主线程),虽然在后台有很多其它java 线程运行例如内存管理,系统管理和单进程等,但是对于应用程序来说-主线程是第一个Java线程并且我们在其中可以创建多线程。多线程是指两个或多个线程同时执行在一个程序中,在单核处理器的计算机中一次只能执行一个线程,操作系统中存在一个“时间切片” 特性用于切分处理器时间在不同的进程和线程中。

Java线程的优势

1.Java线程是轻量级的相比进程来说,当创建一个线程是可以花费少的时间和资源。
2.线程共享它们父进程的数据和代码。
3.上下文切换中通常线程比进程代价更少。
4.线程的内部通讯中是相对容易的比进程通讯。

**Java提供两种方式编写创建线程

1.实现 the java.lang.Runnable 接口.**

确保这个类是可以运行的,我们可以实现java.lang.Runnable中的public void run()方法,为了使得类作为一个线程,我们需要创建一个线程对象,通过这个实例化的对象,然后调用start()方法为了来执行run()方法在一个单独的线程中。
这是一个 java thread 举例 通过实现 Runnable 接口.

package com.journaldev.threads;
public class HeavyWorkRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Doing heavy processing - START "+Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
            //Get database connection, delete unused data from DB
            doDBProcessing();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Doing heavy processing - END "+Thread.currentThread().getName());
    }
    private void doDBProcessing() throws InterruptedException {
        Thread.sleep(5000);
    }
}

2.继承 the java.lang.Thread 类.

我们能够继承java.lang.Thread类为了创建我们自己java线程类并且重写run()方法,同时我们能够创建这个对象和调用start()方法为了执行我们自定义的java线程类run()方法。

这是一个 java thread 举例说明了如何继承Thread类

package com.journaldev.threads;
public class MyThread extends Thread {
 public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println("MyThread - START "+Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
            //Get database connection, delete unused data from DB
            doDBProcessing();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("MyThread - END "+Thread.currentThread().getName());
    }
    private void doDBProcessing() throws InterruptedException {
        Thread.sleep(5000);

    }
}

小结

  1. 线程就是独立执行路径;
  2. 在程序运行时,即使没有自己创建线程,后台也会有对个线程,如主线程,gc线程;
  3. main() 称之为主线程,为系统的入口,用于执行整个程序;
  4. 在一个进程当中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的;
  5. 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
  6. 线程会带来额外的开销,如CPU调度时间,并发控制开销;
  7. 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。

2、线程创建

  • Thread
  • Runnable
  • Callable

Thread

package com.q;
/*
* 创建线程方式一
* 继承Thread
* 重写run()方法
* 调用Start()方法,开启线程
*
* 测试作用:
*       可以看出线程开启不一定立即执行,由CPU调度执行*/
public class TestThread extends Thread{
    @Override
    public void run() {
        for (int i=0;i<=1000;i++){
            System.out.println(i+" 我在看代码   thread");
        }
    }

    public static void main(String[] args) {
        TestThread testThread = new TestThread();
        testThread.start();
        //testThread.run();


        for (int i=0;i<=10;i++){
            System.out.println(i+" ****   thread+++++");
        }
    }
}

下载图片实现

package com.q;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

/*
* 实现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 webDownLoadm = new WebDownLoad();
         webDownLoadm.downloader(url,name);
        System.out.println(url + " "+name);

    }

    public static void main(String[] args) {
        TestThread2 testThread1 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016",
                "中秋1.png");
        TestThread2 testThread2 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016",
                "中秋2.png");
        TestThread2 testThread3 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016",
                "中秋3.png");
        testThread1.start();
        testThread2.start();
        testThread3.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方法出现问题");
        }

    }
}

Runnable

package com.q.runable;

import com.q.thread.TestThread;
/*
* 创建线程方式2
* 实现Runnable接口
* 重写run()方法
* 执行线程需要丢入Runnable接口的实现类,调用start方法
* */
public class TestRunnable implements Runnable{
    @Override
    public void run() {
        for (int i=0;i<=100;i++){
            System.out.println(i+" 我在看代码   thread");
        }
    }

    public static void main(String[] args) {
        //创建Runnable接口的实现类

         TestRunnable testRunnable = new TestRunnable();
         //创建线程对象,通过线程对象来开启线程,代理
         Thread thread = new Thread(testRunnable);
         //开启线程
        thread.start();


        for (int i=0;i<=1000;i++){
            System.out.println(i+" ****   thread+++++");
        }
    }
}

模拟购票

代码

package com.q.并发;

public class Ticket implements Runnable{
    private Integer ticketNum= 20;

    @Override
    public void run() {
        while(true){
            if(ticketNum<=0){
                break;
            }
            //测试
            if (ticketNum.equals(0)){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"--拿到了第"+ticketNum--+"张票");

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        //线程对象
        Ticket ticket = new Ticket();
        //启动线程
        new Thread(ticket,"青岚").start();
        new Thread(ticket,"卿云").start();
        new Thread(ticket,"飞羽").start();
    }
}

运行结果

青岚--拿到了第20张票
飞羽--拿到了第18张票
卿云--拿到了第19张票
青岚--拿到了第17张票
飞羽--拿到了第16张票
卿云--拿到了第15张票
青岚--拿到了第14张票
卿云--拿到了第13张票
飞羽--拿到了第13张票
青岚--拿到了第12张票
卿云--拿到了第11张票
飞羽--拿到了第10张票
青岚--拿到了第9张票
卿云--拿到了第8张票
飞羽--拿到了第7张票
青岚--拿到了第6张票
卿云--拿到了第5张票
飞羽--拿到了第4张票
卿云--拿到了第3张票
青岚--拿到了第2张票
飞羽--拿到了第1张票

Process finished with exit code 0

3、案列-模拟龟兔赛跑

代码(一版)

package com.q.龟兔;

public class Rose implements Runnable{

    private static String winner;
    @Override
    public void run() {
        for (int i=0;i<=100;i++){
            boolean flag= gameOver(i);
            if(flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");

        }

    }
    private boolean gameOver(Integer steps){
        if(winner!=null){
            return true;
        }
        else{
            if (steps>=100){
                winner = Thread.currentThread().getName();
                System.out.println("胜利者是:"+winner);
                return false;
            }
        }
        return false;
    }

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

结果

乌龟-->跑了0步
兔子-->跑了0步
乌龟-->跑了1步
兔子-->跑了1步
乌龟-->跑了2步
兔子-->跑了2步
乌龟-->跑了3步
兔子-->跑了3步
乌龟-->跑了4步
兔子-->跑了4步
乌龟-->跑了5步
兔子-->跑了5步
乌龟-->跑了6步
兔子-->跑了6步
乌龟-->跑了7步
兔子-->跑了7步
乌龟-->跑了8步
兔子-->跑了8步
乌龟-->跑了9步
兔子-->跑了9步
乌龟-->跑了10步
兔子-->跑了10步
乌龟-->跑了11步
乌龟-->跑了12步
兔子-->跑了11步
兔子-->跑了12步
兔子-->跑了13步
乌龟-->跑了13步
乌龟-->跑了14步
兔子-->跑了14步
乌龟-->跑了15步
兔子-->跑了15步
兔子-->跑了16步
兔子-->跑了17步
兔子-->跑了18步
兔子-->跑了19步
兔子-->跑了20步
兔子-->跑了21步
兔子-->跑了22步
兔子-->跑了23步
兔子-->跑了24步
兔子-->跑了25步
兔子-->跑了26步
兔子-->跑了27步
兔子-->跑了28步
兔子-->跑了29步
兔子-->跑了30步
乌龟-->跑了16步
乌龟-->跑了17步
乌龟-->跑了18步
兔子-->跑了31步
乌龟-->跑了19步
兔子-->跑了32步
乌龟-->跑了20步
兔子-->跑了33步
乌龟-->跑了21步
兔子-->跑了34步
乌龟-->跑了22步
兔子-->跑了35步
乌龟-->跑了23步
兔子-->跑了36步
乌龟-->跑了24步
兔子-->跑了37步
乌龟-->跑了25步
兔子-->跑了38步
乌龟-->跑了26步
兔子-->跑了39步
乌龟-->跑了27步
兔子-->跑了40步
乌龟-->跑了28步
兔子-->跑了41步
兔子-->跑了42步
兔子-->跑了43步
兔子-->跑了44步
兔子-->跑了45步
兔子-->跑了46步
兔子-->跑了47步
兔子-->跑了48步
兔子-->跑了49步
兔子-->跑了50步
兔子-->跑了51步
兔子-->跑了52步
兔子-->跑了53步
乌龟-->跑了29步
兔子-->跑了54步
兔子-->跑了55步
乌龟-->跑了30步
乌龟-->跑了31步
兔子-->跑了56步
乌龟-->跑了32步
兔子-->跑了57步
兔子-->跑了58步
兔子-->跑了59步
乌龟-->跑了33步
兔子-->跑了60步
兔子-->跑了61步
兔子-->跑了62步
兔子-->跑了63步
兔子-->跑了64步
乌龟-->跑了34步
兔子-->跑了65步
乌龟-->跑了35步
兔子-->跑了66步
乌龟-->跑了36步
兔子-->跑了67步
乌龟-->跑了37步
兔子-->跑了68步
乌龟-->跑了38步
兔子-->跑了69步
乌龟-->跑了39步
兔子-->跑了70步
乌龟-->跑了40步
兔子-->跑了71步
乌龟-->跑了41步
兔子-->跑了72步
乌龟-->跑了42步
兔子-->跑了73步
乌龟-->跑了43步
兔子-->跑了74步
乌龟-->跑了44步
兔子-->跑了75步
乌龟-->跑了45步
兔子-->跑了76步
乌龟-->跑了46步
兔子-->跑了77步
乌龟-->跑了47步
兔子-->跑了78步
乌龟-->跑了48步
兔子-->跑了79步
乌龟-->跑了49步
兔子-->跑了80步
乌龟-->跑了50步
兔子-->跑了81步
乌龟-->跑了51步
兔子-->跑了82步
乌龟-->跑了52步
兔子-->跑了83步
乌龟-->跑了53步
兔子-->跑了84步
乌龟-->跑了54步
兔子-->跑了85步
乌龟-->跑了55步
兔子-->跑了86步
乌龟-->跑了56步
兔子-->跑了87步
乌龟-->跑了57步
乌龟-->跑了58步
乌龟-->跑了59步
兔子-->跑了88步
乌龟-->跑了60步
兔子-->跑了89步
乌龟-->跑了61步
兔子-->跑了90步
乌龟-->跑了62步
兔子-->跑了91步
乌龟-->跑了63步
兔子-->跑了92步
乌龟-->跑了64步
兔子-->跑了93步
乌龟-->跑了65步
兔子-->跑了94步
乌龟-->跑了66步
兔子-->跑了95步
乌龟-->跑了67步
兔子-->跑了96步
乌龟-->跑了68步
兔子-->跑了97步
乌龟-->跑了69步
兔子-->跑了98步
乌龟-->跑了70步
兔子-->跑了99步
乌龟-->跑了71步
胜利者是:兔子
兔子-->跑了100步

Process finished with exit code 0

升级版(兔子休眠)

package com.q.龟兔;

public class Rose implements Runnable{

    private static String winner;
    @Override
    public void run() {
        for (int i=0;i<=100;i++){
            if (Thread.currentThread().getName().equals("兔子")&&i%20==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(Integer steps){
        if(winner!=null){
            return true;
        }
        else{
            if (steps>=100){
                winner = Thread.currentThread().getName();
                System.out.println("胜利者是:"+winner);
                return false;
            }
        }
        return false;
    }

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

结果

乌龟-->跑了0步
乌龟-->跑了1步
乌龟-->跑了2步
乌龟-->跑了3步
乌龟-->跑了4步
乌龟-->跑了5步
乌龟-->跑了6步
乌龟-->跑了7步
兔子-->跑了0步
乌龟-->跑了8步
兔子-->跑了1步
乌龟-->跑了9步
兔子-->跑了2步
乌龟-->跑了10步
兔子-->跑了3步
乌龟-->跑了11步
兔子-->跑了4步
乌龟-->跑了12步
兔子-->跑了5步
乌龟-->跑了13步
兔子-->跑了6步
乌龟-->跑了14步
兔子-->跑了7步
乌龟-->跑了15步
兔子-->跑了8步
乌龟-->跑了16步
兔子-->跑了9步
乌龟-->跑了17步
兔子-->跑了10步
乌龟-->跑了18步
兔子-->跑了11步
乌龟-->跑了19步
兔子-->跑了12步
乌龟-->跑了20步
兔子-->跑了13步
乌龟-->跑了21步
兔子-->跑了14步
乌龟-->跑了22步
兔子-->跑了15步
乌龟-->跑了23步
兔子-->跑了16步
乌龟-->跑了24步
兔子-->跑了17步
乌龟-->跑了25步
兔子-->跑了18步
乌龟-->跑了26步
兔子-->跑了19步
乌龟-->跑了27步
乌龟-->跑了28步
乌龟-->跑了29步
乌龟-->跑了30步
乌龟-->跑了31步
乌龟-->跑了32步
乌龟-->跑了33步
乌龟-->跑了34步
乌龟-->跑了35步
乌龟-->跑了36步
乌龟-->跑了37步
乌龟-->跑了38步
乌龟-->跑了39步
乌龟-->跑了40步
乌龟-->跑了41步
乌龟-->跑了42步
乌龟-->跑了43步
乌龟-->跑了44步
乌龟-->跑了45步
乌龟-->跑了46步
乌龟-->跑了47步
乌龟-->跑了48步
乌龟-->跑了49步
乌龟-->跑了50步
乌龟-->跑了51步
乌龟-->跑了52步
乌龟-->跑了53步
乌龟-->跑了54步
乌龟-->跑了55步
乌龟-->跑了56步
乌龟-->跑了57步
乌龟-->跑了58步
乌龟-->跑了59步
乌龟-->跑了60步
乌龟-->跑了61步
乌龟-->跑了62步
乌龟-->跑了63步
乌龟-->跑了64步
乌龟-->跑了65步
乌龟-->跑了66步
乌龟-->跑了67步
乌龟-->跑了68步
乌龟-->跑了69步
乌龟-->跑了70步
乌龟-->跑了71步
乌龟-->跑了72步
乌龟-->跑了73步
乌龟-->跑了74步
乌龟-->跑了75步
乌龟-->跑了76步
乌龟-->跑了77步
乌龟-->跑了78步
乌龟-->跑了79步
乌龟-->跑了80步
乌龟-->跑了81步
乌龟-->跑了82步
乌龟-->跑了83步
乌龟-->跑了84步
乌龟-->跑了85步
乌龟-->跑了86步
乌龟-->跑了87步
兔子-->跑了20步
乌龟-->跑了88步
兔子-->跑了21步
乌龟-->跑了89步
兔子-->跑了22步
乌龟-->跑了90步
兔子-->跑了23步
乌龟-->跑了91步
兔子-->跑了24步
乌龟-->跑了92步
兔子-->跑了25步
乌龟-->跑了93步
兔子-->跑了26步
乌龟-->跑了94步
兔子-->跑了27步
乌龟-->跑了95步
兔子-->跑了28步
乌龟-->跑了96步
兔子-->跑了29步
乌龟-->跑了97步
兔子-->跑了30步
乌龟-->跑了98步
兔子-->跑了31步
乌龟-->跑了99步
兔子-->跑了32步
胜利者是:乌龟
乌龟-->跑了100步

Process finished with exit code 0

4、Callable接口

介绍

多线程之callable详解
面试有人会问:线程的实现方式有几种?
很多人可能回答:2种,继承Thread类,实现Runnable接口。
很多忽略了callable这种方式。
也许有人知道callable,也知道callable和Runnable的区别是callable可以有返回值,也可以抛出异常的特性,而Runnable没有。

这里估计很多人懵逼,接下来我们就从源码层次讲解这个问题。

注意callable可以有返回值,也可以抛出异常这点很关键。
很多时候我们让多线程去帮我们处理事情,是需要拿到返回值的,有了异常也可以处理,比如某宝的APP页面,一个页面展示3个块,而每个块展示的信息从后端获取的接口都不一样,那么是让前端调后端3次接口吗?
肯定不行,后端可以把3个块的信息,包装成一个接口,全部返回,那么问题来了,后端调用3个接口,比如第一个接口需要1秒,第二个需要2秒,第三个需要3秒,那么包装的这个接口响应时间最少6秒,怎么解决这个问题呢,可以用多线程来帮我们解决。
启动3个线程,每个线程去调用一个接口,那么3个线程一共执行完的时间就是最慢的那个线程的执行时间,这样接口的响应时间就变成了3秒,一下节省了一半的时间。
那么问题来了,线程如何把执行的业务代码的结果返回来呢?这时候就用到callable了。

实现Callable接口

  1. 实现Callable接口,需要返回值类型;
  2. 重写Call()方法,需要抛出异常;
  3. 创建目标对象;
  4. 创建执行服务:
  5. 提交执行:
  6. 获取结果:
  7. 关闭服务:

代码

package com.q.Call;

import com.q.thread.TestThread2;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.sql.PreparedStatement;
import java.util.concurrent.*;

import static java.util.concurrent.Executors.*;

/*
* 实现线程方式三
* 编写图片下载器
* */
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() throws Exception {
         WebDownLoad webDownLoadm = new  WebDownLoad();
        webDownLoadm.downloader(url,name);
        System.out.println(url + " "+name);
        return true;
    }

    public static void main(String[] args) {
         TestCallable c1 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016",
                "中秋4.png");
        TestCallable c2 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016",
                "中秋5.png");
        TestCallable c3 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016",
                "中秋6.png");
        //创建执行服务
        ExecutorService service = newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1 = service.submit(c1);
        Future<Boolean> r2 = service.submit(c2);
        Future<Boolean> r3 = service.submit(c3);
        //获取结果
        try {
            boolean b1 = r1.get();
            boolean b2 = r2.get();
            boolean b3 = r3.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //关闭服务
        service.shutdown();


    }
}
/*
 * 下载图片类
 * */
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方法出现问题");
        }

    }
}

结果

https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016 中秋6.png
https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016 中秋5.png
https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016 中秋4.png

Process finished with exit code 0

5、线程的静态代理

演示

  • 你:真实角色
  • 婚庆公司:代理你,处理结婚相关事宜
  • 结婚:实现结婚接口即可

代码

package com.q.staticProxy;

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表达式

Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)

Lambda表达式是JAVA8中提供的一种新的特性,它支持JAVA也能进行简单的“函数式编程”。

它是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。

传统的类结构:

package com.q.lamda;
/*
* 推导Lamda表达式
* */
public class TestLamda {
    public static void main(String[] args) {
        LamdaLike lamdaLike = new Like();
        lamdaLike.lamda();
    }
}
/*
* 定义一个函数接口
* */
interface LamdaLike{
    void lamda();
}
class Like implements LamdaLike{
    @Override
    public void lamda() {
        System.out.println("I am a Lamda ");
    }
}

升级版:

package com.q.lamda;
/*
* 推导Lamda表达式
* */
public class TestLamda {
           /* //定义静态内部类
           * */
    static class Like2 implements LamdaLike{
        @Override
        public void lamda() {
            System.out.println("I am a Lamda2");
        }
    }
    public static void main(String[] args) {
        LamdaLike lamdaLike = new Like();
        lamdaLike.lamda();

        lamdaLike = new Like2();
        lamdaLike.lamda();
            /*\
            * 局部类
            * */
        class Like3 implements LamdaLike{
            @Override
            public void lamda() {
                System.out.println("I am a Lamda3");
            }
        }
        lamdaLike = new Like3();
        lamdaLike.lamda();

        /*
        * 匿名内部类
        * 必须借助接口或者父类
        * */
        lamdaLike = new Like(){
            @Override
            public void lamda() {
                System.out.println("I am a Lamda4");
            }
        };
        lamdaLike.lamda();

        /*
        * Lamdba 简化
        * */
        lamdaLike = ()->{
            System.out.println("I am a Lamdba5");
        };
        lamdaLike.lamda();

    }
}
/*
* 定义一个函数接口
* */
interface LamdaLike{
    void lamda();
}
class Like implements LamdaLike{
    @Override
    public void lamda() {
        System.out.println("I am a Lamda ");
    }
}

避免代码过多,过多的匿名内部类;

案列(带参数)

package com.q.lamda;

public class TestLamdba {
    static class Love1 implements LamdbaLove{
        @Override
        public void love(String x, String y) {
            System.out.println(x+"@@"+y);
        }
    }
    public static void main(String[] args) {
        LamdbaLove love = new Love();
        love.love("青岚","卿云");

        love = new Love1();
        love.love("青岚","卿云");

        love = new LamdbaLove() {
            @Override
            public void love(String x, String y) {
                System.out.println(x+"@@@"+y+"匿名内部类");
            }
        };
        love.love("青岚","卿云");

        love = (String x,String y)->{
            System.out.println(x+"@@@"+y+"Lamdba表达式");
        };
        love.love("青岚","卿云");

        /*
        * 去掉参数类型
        * */
        LamdbaLove love1 = (x,y)->{
            System.out.println(x+"@@@"+y+"Lamdba表达式");
        };
        love1.love("青岚","卿云");

    }
}
interface LamdbaLove{
    void love(String x,String y);
}
class Love implements LamdbaLove{
    @Override
    public void love(String x, String y) {
        System.out.println(x+"@"+y);
    }
}

7、线程状态

线程有五个状态

线程停止

package com.q.线程状态;
/*
* 线程测试stop
* 建议线程自动停止,---》利用次数,不建议死循环
* 不使用JDK不建议使用的方法
* */
public class TestStop implements Runnable{
    //设置表示位
    private boolean flag = true;

    @Override
    public void run() {
        int i=0;
        while(flag){
            System.out.println("run.....Thread"+i++);
        }
    }

    public void stop(){
        this.flag=false;

    }

    public static void main(String[] args) {
        TestStop testStop = new TestStop();

        Thread thread = new Thread(testStop);
        thread.start();

        for (int i=0;i<1000;i++){
            System.out.println(i);
            if( i==900 ){
                //调用stop方法切换标志位,停止线程
                testStop.stop();
                System.out.println("停止线程");
                break;
            }
        }
    }
}

线程休眠

package com.q.线程状态;

import java.text.SimpleDateFormat;
import java.util.Date;

/*
* 模拟倒计时
* */
public class TestSleep {
    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();
            }
        }

    }

    public static void tenDown(){
        int num = 10;
        while(true){
            try {
                Thread.sleep(1000);
                System.out.println(num);
                num--;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(num==0){
                break;
            }
        }

    }

}

线程礼让

yield

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转为就绪状态
  • 让CPU重新调度,礼让不一定成功!看CPU心情
package com.q.线程状态;
/*
* 测试礼让线程
* */
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield,"卿云").start();
        new Thread(myYield,"青岚").start();
    }

}

class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}

线程强制执行

package com.q.线程状态;

import org.junit.rules.TestRule;

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) {
        TestJoin testJoin = new TestJoin();

        Thread thread = new Thread(testJoin);
        thread.start();

        for (int i = 0; i < 50; i++) {
            if(i==30){
                try {
                    thread.join();
                    System.out.println("------------------");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("main()"+i);
        }
    }
}

观测线程状态

package com.q.线程状态;

public class TestState {

    public static void main(String[] args) throws InterruptedException {
        Runnable target;
        Thread thread = new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("*****************");
            }
        });

        //观察状态
        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(100);
            state = thread.getState();
            System.out.println(state);
            
        }
    }
}

结果:

NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
*****************
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
*****************
TERMINATED

8、线程的优先级

package com.q.priority;

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(4);

        t3.setPriority(5);
        t3.start();

        t4.start();

        t5.setPriority(3);
        t5.start();

        t6.setPriority(Thread.MAX_PRIORITY);
        t6.start();
    }
}


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

    }
}

9、守护线程

daemon

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如,后台记录操作日志,监控内存,垃圾回收等待

10、线程同步机制

并发: 同一个对象被多个线程同时操作

要说明线程同步问题首先要说明Java线程的两个特性,可见性和有序性。多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现。拿上篇博文中的例子来说明,在多个线程之间共享了Count类的一个对象,这个对象是被创建在主内存(堆内存)中,每个线程都有自己的工作内存(线程栈),工作内存存储了主内存Count对象的一个副本,当线程操作Count对象时,首先从主内存复制Count对象到工作内存中,然后执行代码count.increment(),改变了num值,最后用工作内存Count刷新主内存Count。当一个对象在多个内存中都存在副本时,如果一个内存修改了共享变量,其它线程也应该能够看到被修改后的值,此为可见性。多个线程执行时,CPU对线程的调度是随机的,我们不知道当前程序被执行到哪步就切换到了下一个线程,一个最经典的例子就是银行汇款问题,一个银行账户存款100,这时一个人从该账户取10元,同时另一个人向该账户汇10元,那么余额应该还是100。那么此时可能发生这种情况,A线程负责取款,B线程负责汇款,A从主内存读到100,B从主内存读到100,A执行减10操作,并将数据刷新到主内存,这时主内存数据100-10=90,而B内存执行加10操作,并将数据刷新到主内存,最后主内存数据100+10=110,显然这是一个严重的问题,我们要保证A线程和B线程有序执行,先取款后汇款或者先汇款后取款,此为有序性。本文讲述了JDK5.0之前传统线程的同步方式,更高级的同步方式可参见Java线程(八):锁对象Lock-同步问题更完美的处理方式。

每个锁对象(JLS中叫monitor)都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个线程被唤醒(notify)后,才会进入到就绪队列,等待CPU的调度,反之,当一个线程被wait后,就会进入阻塞队列,等待下一次被唤醒,这个涉及到线程间的通信,下一篇博文会说明。看我们的例子,当第一个线程执行输出方法时,获得同步锁,执行输出方法,恰好此时第二个线程也要执行输出方法,但发现同步锁没有被释放,第二个线程就会进入就绪队列,等待锁被释放。一个线程执行互斥代码过程如下:

1.获得同步锁;
2. 清空工作内存;
3. 从主内存拷贝对象副本到工作内存;
4. 执行代码(计算或者输出等);
5. 刷新主内存数据;
6. 释放同步锁。

所以,synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性。

同步锁

ava 中的 synchronized 关键字可以在多线程环境下用来作为线程安全的同步锁。本文主要对 synchronized 的作用,以及其有效范围进行讨论。
Java中的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。

synchronized 关键字主要有以下几种用法:

  • 非静态方法的同步;
  • 静态方法的同步;
  • 代码块。

下面分对象锁和类锁来分别说明 synchronized 用法:

对象锁

非静态方法使用 synchronized 修饰的写法,修饰实例方法时,锁定的是当前对象:

    public synchronized void test(){
        // TODO
    }

代码块使用 synchronized 修饰的写法,使用代码块,如果传入的参数是 this,那么锁定的也是当前的对象:

    public void test(){
        synchronized (this) {
            // TODO
        }
    }

下面通过例子来说明对象锁:
定义一个类,方法如下,将 count 自减,从 5 到 0:

public class TestSynchronized {

public synchronized void minus() {
    int count = 5;
    for (int i = 0; i < 5; i++) {
        count--;
        System.out.println(Thread.currentThread().getName() + " - " + count);
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
        }
    }
}

}

测试调用方法如下:

public class Run {

public static void main(String[] args) {

​    final TestSynchronized test = new TestSynchronized();

​    Thread thread1 = new Thread(new Runnable() {

​        @Override
​        public void run() {
​            test.minus();
​        }
​    });

​    Thread thread2 = new Thread(new Runnable() {

​        @Override
​        public void run() {
​            test.minus();
​        }
​    });

​    thread1.start();
​    thread2.start();

}

}

两个线程 thread1 和 thread2,同时访问对象的方法,由于该方法是 synchronized 关键字修饰的,那么这两个线程都需要获得该对象锁,一个获得后另一个线程必须等待。所以我们可以猜测运行结果应该是,一个线程执行完毕后,另一个线程才开始执行

案例(购票)

package syn;

public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"青岚").start();
        new Thread(buyTicket,"卿云").start();
        new Thread(buyTicket,"致非").start();
    }

}

class BuyTicket implements Runnable{
    private Integer ticketNum=10;
    boolean flag = true;


    @Override
    public void run() {
       //买票
       while(flag){
           try {
               Thread.sleep(100);
               buy();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }

       }
    }

    private synchronized void buy(){
        if (ticketNum<=0){
            return;
        }
        System.out.println(Thread.currentThread().getName()+"-->拿到"+ticketNum--);
    }
}

结果

卿云-->拿到10
青岚-->拿到9
致非-->拿到8
卿云-->拿到7
青岚-->拿到6
致非-->拿到5
卿云-->拿到4
青岚-->拿到3
致非-->拿到2
卿云-->拿到1

同步锁代码

package Lock;

import java.util.List;

/*死锁
* 多个线程互相拥有资源,对方却无法使用
* */
public class DeadLock {
    public static void main(String[] args) {
        MakeUp g1 = new MakeUp(1,"青岚");
        MakeUp g2 = new MakeUp(0,"卿云");

        g1.start();
        g2.start();
    }
}


class Lipstick{

}

class Mirror {

}

class MakeUp extends Thread{
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choise;
    String name;

    public MakeUp(int choise, String name) {
        this.choise = choise;
        this.name = name;
    }

    @Override
    public void run() {

        makeUp();
    }

    //互相持有对方的锁
    private void makeUp(){
        if (choise==0){
            synchronized (lipstick){//获得口红的锁
                System.out.println("获得口红的锁");
                try {
                    Thread.sleep(1000);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (mirror){
                System.out.println("获得镜子的锁");
            }
        }else {
            synchronized (mirror){//获得口红的锁
                System.out.println("获得镜子的锁");
                try {
                    Thread.sleep(1000);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (lipstick){
                System.out.println("获得口红的锁");
            }
        }
    }
}

11、Lock 锁

12、线程协作

生产者与消费者模式

posted @ 2021-09-20 16:01  致非  阅读(238)  评论(0)    收藏  举报