最全多线程知识点集结

程序 进程 线程的理解

1.程序(program)
是为了完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码.

2.进程(process)
概念:正在运行的程序;
说明:进程是资源分配的单位,每个进程会得到系统分配的不同的内存区域;

3.线程(thread)
概念:线程是进程的进一步细化,是程序执行的一条路径.
说明: 进程可以细化为多个进程
每个线程,拥有自己独立的:虚拟机栈, 程序计数器
多个线程,共享同一个进程中的结构:方法区,堆
一个Java应用程序java.exe,至少有三个线程:main()主线程,垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。

4.并行与并发的理解
并行:多个CPU同时执行多个任务;例如:多个人同时做不同的事.
并发: 一个CPU同时执行多个任务;例如:秒杀,多个人同时做一件事.

线程创建的方式

方式一:继承于Thread

1.创建一个继承于Thread类的子类
2.重写Thread中的run()需要执行的操作
3.创建子类对象,调用start();作用:①启动当前线程②调用当前线程的run()

  • 问题一:我们不能通过直接调用run()的方式启动线程。
  • 问题二:再启动一个线程,不可以让已经start()的程序去执行,会报错IllegalThreadStateException
  • 需要重新创建新的线程对象
//1.创建一个继承于Thread类的子类
class MyThread extends Thread {
    //2.重写Thread 中的run()
    public void run(){
        for (int i = 0; i < 100; i++) {
            if(i%2==0){
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        MyThread t=new MyThread();
        t.start();
        //如下的是在main线程中执行的
        for (int i = 0; i < 100; i++) {
            if(i%2==0)               System.out.println(Thread.currentThread().getName()+i+"****main****");
            }
        }
    }
}

方式二:实现Runnable接口

  • 1.创建一个实现接口Runnable的类;
  • 2.实现类去实现Runnable中的抽象方法run();
  • 3.创建实现类对象
  • 4.将此对象作为参数传给Thread中的构造器中,创建Thread对象;
  • 5.通过Thread类的对象调用start;①启动线程②调用当前线程的run()–>调用的是Runnable类型的target的run()方法

比较创建线程的两种方式

  • 开发中:优先选择:实现Runnable接口的方式
  • 原因:1.实现的方式没有类的单继承的局限性
  • 2.实现的方式更适合来处理多个线程有共享数据的情况
    联系:
  • public class Thread implements Runnable
  • 相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中
ublic class ThreadTest1 {
    public static void main(String[] args) {
        Threadjk p = new Threadjk();
        Thread t1 = new Thread(p);
        t1.setName("线程1");
        t1.start();
        //再启动一个线程
        Thread t2 = new Thread(p);
        t2.setName("线程2");
        t2.start();
    }
}
class Threadjk implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if(i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }

    }
}

Thread类中的常用方法

测试Thread中常用的方法

  • 1.start():启动当前线程,调用当前线程的run();
  • 2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明再此方法中
  • 3.currentThread():静态方法,返回执行当前代码的线程
  • 4.getName():获取当前线程的名字
  • 5.setName():设置当前线程的名字
  • 6.yield():释放当前CPU的执行权
  • 7.join():在线程A中调用线程B的join(),此时线程A进入阻塞状态,直到线程B完全执行完以后,线程A才结束阻塞状态;
  • 8.stop():已过时,当执行此方法时,强制结束线程
  • 9.sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒,在指定的毫秒数内,当前线程处于阻塞状态
  • 10.isAlive():判断当前线程是否存活;

1.线程的优先级:

  • NORM_PRIORITY = 5;–>默认优先级
  • MIN_PRIORITY = 1;
  • MAX_PRIORITY = 10;

2.如何获取和设置当前线程的优先级:

  • getPriority():获取线程的优先级
  • setPriority(int p):设置线程的优先级

说明:高优先级的线程要抢占低优先级线程CPU的执行权,但是只是从概率上讲,高优先级的线程高概率的情况下被执行,并不意味着

  • 只有高优先级的线程执行结束后,再执行低优先级的线程

线程的生命周期

在这里插入图片描述
说明:
1.生命周期关注两个概念:状态、相应的方法
2.关注:状态a–>状态b:哪些方法执行了(回调方法)
某个方法主动调用:状态a–>状态b
3.阻塞:临时状态,不可以作为最终状态
死亡:最终状态。

线程的同步机制

方式一:同步代码块

  synchronized(同步监视器){
        //需要被同步的代码
  }

说明:1.操作共享数据的代码,即为需要被同步的代码。 -->不能包含代码多了,也不能包含代码少了。
2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。
3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
要求:多个线程必须要共用同一把锁。

  • 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
    在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。
public void run() {
        while(true){
            synchronized(this) {//this 表示Dow 对象
                if (sticke > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ",票号为:" + sticke);
                    sticke--;
                } else {
                    break;
                }
            }

方式二:同步方法

如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。

关于同步方法的总结:

  • 1.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
  • 2.非静态的同步方法,同步监视器是:this
  • 3 静态的同步方法,同步监视器是:当前类本身
public synchronized void show(){//同步监视器 this 当前实现类对象
    if (sticke > 0) {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ",票号为:" + sticke);
        sticke--;
    }
}

方式三:lock(锁)–开发中优先使用 JDK 5.0 新增

  • 1.实例化Reentrantlock
  • 2.调用lock()锁定方法
  • 3.调用unlock()解锁方法
class Windo implements Runnable{
    private int ticke=100;
    private   ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true){
            try {
                lock.lock();
                if (ticke > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":票号:" + ticke);
                    ticke--;
                } else {
                    break;
                }
            }finally{
                lock.unlock();
            }
        }
    }
}
public class WindowTest3 {
    public static void main(String[] args) {
        Windo t = new Windo();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        t1.start();
        t2.start();
        t3.start();
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
    }
}

面试题:synchronized和lock的异同?

  • 1.相同点:都能解决线程安全问题
  • 2.不同点:synchronized机制在执行完同步代码之后自动释放同步监视器
  • lock 需要手动的启动同步(lock()),同时结束同步也需要手动实现(unlock())

面试题:解决线程安全问题有几种方法?
有三种分别是:

  • 1.同步代码块
  • 2.同步方法
  • 3.lock锁

信息通讯测试;

两个线程 交替输出1-100

  • 1.wait():一旦执行此方法,当前线程就进入阻塞状态并释放同步监视器
  • 2.notify():一旦执行此方法,就会唤醒被wait()的一个线程,如果有多个线程被wait(),就唤醒优先级高的;
  • 3.notifyAll():一旦执行此方法,就会唤醒被wait()的所有线程

说明:
1.wait()\notify()\notifyAll()三个方法使用必须在同步代码块内或同步方法内
2.wait()\notify()\notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器
否则会报错:IllegalMonitorStateException
3.wait()\notify()\notifyAll()三个方法声明在Object类中

class Num implements Runnable {
    private int p = 1;

    @Override
    public void run() {
//        Object obj=new Object();
        while (true) {
            synchronized (this) {

                notify();
                if (p <= 100) {
                    System.out.println(Thread.currentThread().getName() + ":" + p);
                    p++;
                } else {
                    break;
                }
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

public class NumberWait {
    public static void main(String[] args) {
        Num t = new Num();
        Thread j = new Thread(t);
        Thread j1 = new Thread(t);
        j.start();
        j1.start();
        j.setName("线程一");
        j1.setName("线程二");
    }
}

jdk5.0新增创建多线程方式

方式三:实现Callable接口

优势:
1.call()可以有返回值
2.call()可以抛出异常,被外面的操作所捕获,获取异常信息;
3.call()支持泛型
实现步骤:

  • 1.创建一个实现Callable的实现类
  • 2.实现call()方法,将此线程需要进行的操作声明在call()方法中
  • 3.创建Callable实现类的对象
  • 4.将此实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象
  • 5.将FutureTask的对象作为参数传递给Thread类的构造器中,创建Thread对象,并调用start();
  • 6.获取Callable中call()方法的返回值
//1.创建一个实现Callable的实现类
class Able implements Callable{
    @Override
    //2.实现call()方法,将此线程需要进行的操作声明在call()方法中
    public Object call() throws Exception {
        int sum=0;
        for (int i = 1; i <=100; i++) {
            if(i%2==0){
                System.out.println(i);
                sum+=i;
            }
        }
        return sum;
    }
}
public class CallableTest {
    public static void main(String[] args) {
        //3.创建Callable实现类的对象
        Able p = new Able();
        //4.将此实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask task = new FutureTask(p);//public class FutureTask<V> implements RunnableFuture<V>
        //5.将FutureTask的对象作为参数传递给Thread类的构造器中,创建Thread对象,并调用start();
        Thread t = new Thread(task);
        t.start();
        try {
            Object sum=task.get();
            System.out.println("总和为:"+sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

方式四:使用线程池

class NumberThread implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

class NumberThread1 implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

public class ThreadPool {

    public static void main(String[] args) {
        //1. 提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //设置线程池的属性
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();


        //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new NumberThread());//适合适用于Runnable
        service.execute(new NumberThread1());//适合适用于Runnable

//        service.submit(Callable callable);//适合使用于Callable
        //3.关闭连接池
        service.shutdown();
    }

}

说明:
好处:

  • 1.提高响应速度(减少了创建新线程的时间)
  • 2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  • 3.便于线程管理
    corePoolSize:核心池的大小
    maximumPoolSize:最大线程数
    keepAliveTime:线程没任务时最多保持多长时间后会终止
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值