0
点赞
收藏
分享

微信扫一扫

面试腾讯,字节跳动首先要掌握的Java多线程,一次帮你全掌握!

沪钢木子 2021-09-30 阅读 89

一、程序,进程,线程联系和区别

进程:每个进程都有独立的代码和数据空间(进程上下文),一个进程包含一个或者多个线程,同时线程是资源分配的最小单位。

线程:同一类线程共享代码和数据空间,并且每个线程有独立运行栈和程序计数器,同时线程是调度的最小单位。

那什么是多进程呢? ,常见的是打开我们自己电脑任务管理器里面就还有多个进程,其实指的是我们的操作系统能同时运行多个任务(微信,QQ等)。

多线程其实就是一个进程有多条路径在运行。

二、多线程实现的方式

在Java中实现多线程有三种方式,第一种方式是继承Thread类,第二种方式是实现Runnable接口, 第三种是实现Callable,结合Future使用(了解),并发编程中使用,这里不详细说:

第一种实现多线程的方式,继承Thread类,代码实现如下:

public class RabbitDemo extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <10 ; i++) {
            System.out.println("兔子跑了:"+i+"步");
        }
    }
}
public class TortoiseDemo extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <10 ; i++) {
            System.out.println("乌龟跑了:"+i+"步");
        }
    }
}
public class ClientThreadTest {
    public static void main(String[] args) {
        RabbitDemo rabbitDemo = new RabbitDemo();
        TortoiseDemo tortoiseDemo = new TortoiseDemo();
        rabbitDemo.start();
        tortoiseDemo.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("main==>" + i);
        }
    }
}


第二个实例实现多个线程同时共享一个成员变量线程的运行状态。

public class Qp12306 implements Runnable {
    private int num=50;
    @Override
    public void run() {
        while (true){
            if (num<=0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"抢到票"+num--);
        }
    }
}
public class ClientQpTest {
    public static void main(String[] args) {
        Qp12306 qp12306 = new Qp12306();
        Thread thread = new Thread(qp12306, "张三");
        Thread thread1 = new Thread(qp12306, "李四");
        Thread thread2 = new Thread(qp12306, "王五");
        thread.start();
        thread1.start();
        thread2.start();
    }
}

第二种方式:实现 Runnable接口,重写run方法,这种方式我们实现多线程常用的方式(避免Java单继承的限制)并且该方式采用了静态代理设计模式(参考: 静态代理),代码实例如下 :

public class RunnableDemo implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("Runnable:" + i);
        }
    }
}
public class ClientRunableTest {
    public static void main(String[] args) {
        RunnableDemo runnableDemo = new RunnableDemo();
        Thread thread = new Thread(runnableDemo);
        thread.start();
    }
}

第三方式:实现Callable接口,结合Future实现多线程,代码实例如下:

/**
 * 使用Callable创建线程
 */
public class Race implements Callable<Integer> {
    private String name;
    private long time; //延时时间
    private boolean flag = true;
    private int step = 0; //步

    @Override
    public Integer call() throws Exception {
        while (flag) {
            Thread.sleep(time);
            step++;
        }
        return step;
    }

    public Race(String name, long time) {
        this.name = name;
        this.time = time;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
public class CallableTest {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Race race = new Race("张三", 200);
        Race race1 = new Race("李四", 500);
        Future<Integer> result = executorService.submit(race);
        Future<Integer> result1 = executorService.submit(race1);
        Thread.sleep(3000);
        race.setFlag(false);
        race1.setFlag(false);
        int num1 = result.get();
        int num2 = result1.get();
        System.out.println("张三-->" + num1 + "步");
        System.out.println("李四-->" + num2 + "步");
        //停止服务
        executorService.shutdownNow();
    }
}

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

总结:

实现Runnable接口比继承Thread类所具有的优势:

1、适合多个相同的程序代码的线程去处理同一个资源

2、可以避免java中的单继承的限制

3、增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

4、线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM就是在操作系统中启动了一个进程。

三、线程的状态和方法

1.新建状态 :线程对象被创建后就进入了新建状态,Thread thread = new Thread();

2.就绪状态(Runnable):也被称之为“可执行状态”,当线程被new出来后,其他的线程调用了该对象的start()方法,即thread.start(),此时线程位于“可运行线程池”中,只等待获取CPU的使用权,随时可以被CPU调用。进入就绪状态的进程除CPU之外,其他运行所需的资源都已经全部获得。

3.运行状态(Running):线程获取CPU权限开始执行。注意:线程只能从就绪状态进入到运行状态。

4.阻塞状态(Bloacked):阻塞状态是线程因为某种原因放弃CPU的使用权,暂时停止运行,知道线程进入就绪状态后才能有机会转到运行状态。

阻塞的情况分三种:

(1)、等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池中”。进入这个状态后是不能自动唤醒的,必须依靠其他线程调用notify()或者notifyAll()方法才能被唤醒。
(2)、同步阻塞:运行的线程在获取对象的(synchronized)同步锁时,若该同步锁被其他线程占用,则JVM会吧该线程放入“锁池”中。
(3)、其他阻塞:通过调用线程的sleep()或者join()或发出了I/O请求时,线程会进入到阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新回到就绪状态

  1. 死亡(Dead):线程执行完了或因异常退出了run()方法,则该线程结束生命周期。

阻塞线程方法的说明:

wait () , sleep()的区别:

四、线程的基本信息和优先级

下面代码实现:
基本信息

public static void main(String[] args) throws InterruptedException {
        Rundemo rundemo = new Rundemo();
        Thread thread = new Thread(rundemo);
        thread.setName("线程"); //设置线程的名称
        System.out.println(thread.getName()); //获取当前线性的名称
        System.out.println(Thread.currentThread().getName()); //main线程
        thread.start();
        System.out.println("线程启动后的状态:" + thread.isAlive());
        Thread.sleep(10);
        rundemo.stop();
        Thread.sleep(1000); //停止后休眠一下,再看线程的状态
        System.out.println("线程停止后的状态:" + thread.isAlive());
        test();

    }

优先级

public static void test() throws InterruptedException {
        Rundemo rundemo1 = new Rundemo();
        Thread thread1 = new Thread(rundemo1);
        thread1.setName("线程thread1");
        Rundemo rundemo2 = new Rundemo();
        Thread thread2 = new Thread(rundemo2);
        thread2.setName("线程thread2");
        thread1.setPriority(Thread.MAX_PRIORITY);
        thread2.setPriority(Thread.MIN_PRIORITY);
        thread1.start();
        thread2.start();
        Thread.sleep(1000);
        rundemo1.stop();
        rundemo2.stop();

    }

五、线程的同步和死锁问题

下面代码实现:多个线程同时访问一份资源使用synchronized

public class Qp12306 implements Runnable {
    private int num = 10;  
    private boolean flag = true;

    @Override
    public synchronized void run() {
        while (true) {
            test();
        }
    }

    public synchronized void test() {
        if (num <= 0) {
            flag = false;
            return;
        }
        System.out.println(Thread.currentThread().getName() + "抢到票" + num--);
    }
}

public class ClientQpTest {
    public static void main(String[] args) {
        Qp12306 qp12306 = new Qp12306();
        Thread thread = new Thread(qp12306, "张三");
        Thread thread1 = new Thread(qp12306, "李四");
        Thread thread2 = new Thread(qp12306, "王五");
        thread.start();
        thread1.start();
        thread2.start();
    }
}

运行结果


synchronized 静态代码块

public class Client {
    public static void main(String[] args) throws InterruptedException {
        JvmThread thread1 = new JvmThread(100);
        JvmThread thread2 = new JvmThread(500);
        thread1.start();
        thread2.start();


    }

}

class JvmThread extends Thread{
    private long time;
    public JvmThread() {
    }
    public JvmThread(long time) {
        this.time =time;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->创建:"+Jvm.getInstance(time));
    }
}


/**
 * 单例设计模式
 * 确保一个类只有一个对象
 * 懒汉式  
 * 1、构造器私有化,避免外部直接创建对象
 * 2、声明一个私有的静态变量
 * 3、创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象
 */
class Jvm {
    //声明一个私有的静态变量
    private static Jvm instance =null;
    //构造器私有化,避免外部直接创建对象
    private Jvm(){

    }
    //创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象
    public static Jvm getInstance(long time){
        if(null==instance){
            synchronized(Jvm.class){
                if(null==instance ){
                    try {
                        Thread.sleep(time); 
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    instance =new Jvm();
                }
            }
        }
        return instance;
    }


    public static Jvm getInstance3(long time){
        synchronized(Jvm.class){
            if(null==instance ){
                try {
                    Thread.sleep(time); 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                instance =new Jvm();
            }
            return instance;
        }
    }
    
}

六、生产者消费者模式

具体实现代码如下:

/**
 * 生产者
 */
public class ProduceRu implements Runnable {
    private Movie movie;

    public ProduceRu(Movie movie) {
        this.movie = movie;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (0 == i % 2) {
                movie.proTest("向往的生活");
            } else {
                movie.proTest("好声音");
            }
        }
    }
}

/**
 *消费者
 */
public class ConsumeRu implements Runnable {
    private Movie movie;

    public ConsumeRu(Movie movie) {
        this.movie = movie;
    }
    @Override
    public void run() {
        for(int i=0;i<10;i++){
            movie.conTest();
        }
    }
}

/**
 * 生产者消费者模式共同访问同一份资源
 * wait() 等等,释放锁,sleep()不释放锁
 * notify()/notifyAll():唤醒
 */
public class Movie {

    private String pic;
    //flag--->true 生产者生产,消费者等待,生产完后通知消费
    //flag--->false 消费者消费,生产者等待,消费完通知生产
    private boolean flag = true;

    /**
     * 生产者生产,消费者等待,生产完后通知消费
     * @param pic
     */
    public synchronized void proTest(String pic) {
        if (!flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("生产了:" + pic);
        this.pic = pic;
        this.notify();
        this.flag = false;

    }

    /**
     * 消费者消费,生产者等待,消费完通知生产
     */
    public synchronized void conTest() {
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("消费者:" + pic);
        this.notifyAll();
        this.flag = true;
    }
}

public class ClientTest {
    public static void main(String[] args) {
        Movie movie=new Movie();
        new Thread(new ProduceRu(movie)).start();
        new Thread(new ConsumeRu(movie)).start();
    }
}

七、任务调度

public class ThreadTest {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("任务开始了");
                    while (true) {
                        Thread.sleep(1000);
                        System.out.println("hello");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

public class TimeTest {

    public static void main(String[] args) {
        Timer timer=new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("任务运行开始.......");
            }
        },new Date(),1000);
    }
}

方法说明:
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit)
创建并执行在给定的初始延迟之后首先启用的定期动作,随后在一个执行的终止和下一个执行的开始之间给定的延迟。 如果任务的执行遇到异常,则后续的执行被抑制。 否则,任务将仅通过取消或终止执行人终止。
参数
command - 要执行的任务
initialDelay - 延迟第一次执行的时间 (就是第一次指定定时延时多久),代码里我延时10秒
delay - 一个执行终止与下一个执行的开始之间的延迟
unit - initialDelay和delay参数的时间单位


public class ScheduledExecutorServiceTest {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("任务!!!!");
            }
        },10000,1000, TimeUnit.MILLISECONDS);
    }
}

总结:看完有什么不懂的欢迎在下方留言评论!

举报

相关推荐

0 条评论