Java之多线程

发布于:2023-01-17 ⋅ 阅读:(376) ⋅ 点赞:(0)

进程和线程

什么是进程:进程是系统进行资源分配单位,是操作系统结构的基础,比如我们在使用微信聊天,当我们启动微信就相当于我们启动了一个进程。

什么是线程:线程是cpu资源调度的最小单位。一个进程最少有一个线程,

通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度,从而显著提高系统资源的利用率和吞吐量。

线程创建的方法 :4种方式

一、编写一个类,直接 继承 java.lang.Thread重写 run方法

  1. 怎么创建线程对象? new继承线程的类。
  2. 怎么启动线程呢? 调用线程对象的 start() 方法。

二、编写一个类,实现 java.lang.Runnable 接口,实现run方法

  1. 怎么创建线程对象? new Thread 传入可运行的类/接口。
  2. 怎么启动线程呢? 调用线程对象的 start() 方法。
  3. 也可以使用匿名内部类方式创建:

三、编写一个类,实现Callable接口(JDK5.0新增)实现call方法

该方法效率较低,因为在获取线程的执行结果的时候,当前线程受阻塞。

但是可以拿到线程的返回结果

//1.创建一个实现Callable的实现类
class NumThread implements Callable{

    //2.实现call方法,将此线程需要执行的操作声明在call()中
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for(int i = 1;i <= 100;i++){//快捷方式:100.for
            if(i % 2 == 0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;


    }
}


public class ThreadNew {
    public static void main(String[] args) {
        //3.创建Callable接口实现类的对象
        NumThread numThread = new NumThread();

        //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask futureTask = new FutureTask(numThread);

        //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
        new Thread(futureTask).start();

        try {
            //6.获取Callable中call方法的返回值
            //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
            Object sum = futureTask.get();
            System.out.println("总和为:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

四、使用线程池创建

对经常大量创建销毁、使用量特别大的资源,比如并发情况下的线程,对性能的影响很大

可以提前创建多个线程,放入线程池,使用时候直接获取,使用完之后放回池中。以避免频繁的创建销毁、实现重复利用率 

好处呢显然就是1、提高响应速度  2、降低资源消耗   3、便于线程管理

线程池各项参数:

corePoolSize 代表核心线程数

maxinumPoolSize 代表的是最大线程数

keepAliveTime 表示超出核心线程数之外的线程的空闲存活时间

workQueue 用来存放待执行的任务

ThreadFactory 实际上是一个线程工厂,用来生产线程执行任务

Handler 任务拒绝策略,有两种情况,第一种是当我们调用 shutdown 等方法关闭线程池后,这 时候即使线程池内部还有没执行完的任务正在执行,但是由于线程池已经关闭,我们再继续想线程 池提交任务就会遭到拒绝。另一种情况就是当达到最大线程数,线程池已经没有能力继续处理新提 交的任务时,这是也就拒绝。

public class ThreadPool {
    public static void main(String[] args) {
        //  ExecutorService executorService = Executors.newFixedThreadPool(10); //制造一个线程池,里面有10个线程
        //注意Executors.newFixedThreadPool是一个接口,需要转换成实现类
        ThreadPoolExecutor executorService = (ThreadPoolExecutor)Executors.newFixedThreadPool(10);
        //设置线程池的属性
        executorService.setCorePoolSize(10);   //代表核心线程数,也就是正常情况下创建工作的线程数,这些线程创建后并不会消除,而是一种常驻线程
        executorService.setMaximumPoolSize(10);  //代表的是最大线程数
        executorService.setKeepAliveTime(1000, TimeUnit.MILLISECONDS);   // 表示超出核心线程数之外的线程的空闲存活时间
        

        executorService.execute(new NumberThread());//适合适用于Runnable
        executorService.execute(new NumberThread2());//适合适用于Runnable
       // executorService.submit();  //适合适用于Callable
        executorService.shutdown();  //关闭线程池
    }
}

线程池执行流程:

​​​​​​​

 

 

 

当多个线程同时对数据进行访问和修改时,可能会出现数据不准确等问题,使用synchroized加互斥锁始终保持同时只有一个线程能够访问,保证数据安全。

synchroized关键字互斥锁

互斥锁 
1.每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
2.关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,。表明该对象在任一时刻只能由一个线程访问
3.同步的局限性:导致程序的执行效率要降低
4.同步方法((非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)

5.同步方法(静态的)的锁为当前类本身

方法:

1、同步代码块

synchronized (对象) {  //得到对象的锁,才能操作同步代码,只要是 同一对象都行
//需要同步的代码
}

2、放在方法上

非静态同步方法锁默认是加到this上

public synchronized void lock(){ //表示方法为同步方法
//需要背同步的代码
}

3、放在静态方法上

注意静态同步方法的锁是加在类名.class,里面代码块也需要类名.class

public static void lock(){ //表示方法为静态同步方法
    synchronized(类名.class){
       //同步代码块内容
    }
}

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到