线程相关笔记

目录

目录

一、线程常用API

二、创建线程的两种方式

第一种:继承Thread类

第二种:实现Runnable接口

附:匿名内部类方式实现上述两种创建线程的方法

第三种:使用Callable接口

三、实现Runnable接口创建多线程程序的好处

四、解决线程安全问题的三种方式

1、同步代码块

2、同步方法

3、Lock锁

五、线程的几种状态

六、线程之间的通信

为什么要处理线程间的通信

如何保证线程间通信有效利用资源

七、线程池

线程池的原理

常用线程池的接口和类

线程池的使用步骤



一、线程常用API

Thread类:String getName()  返回此线程的名称

setName(String name) 设置此线程的名称为name

public static void sleep(long millis) 是当前正在执行的线程以指定的毫秒数暂停,也就是睡眠

public static void yield() 主动放弃时间片,回到就绪状态,等待参与下一次竞争

线程对象. join() 加入当前线程(主线程),并阻塞当前线程,直到加入的线程执行完毕

线程对象. setPriority(5) 这里参数默认为5,优先级为1-10级,越大越优先

线程对象. setDaemon(True) 设置该线程为守护线程(后台线程),要在start()之前指定。[垃圾回收线程属于守护线程]

 

二、创建线程的两种方式

第一种:继承Thread类

     1、创建一个继承Thread的子类

     2、在子类中重写Thread类中的run方法,设置线程任务

     3、创建一个子类实现对象

     4、用子类对象调用start方法,就开启了新线程,执行run方法

代码实现:

public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("这里是线程任务!");
    }
}

public class Demo {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
    }
}

 

第二种:实现Runnable接口

  1. 创建一个实现Runnable接口的实现类
  2. 重写Runnable接口中的run方法,设置线程方法
  3. 创建一个实现类的对象
  4. 创建一个Thread类对象,构造方法中将实现类对象作为参数
  5. 使用Thread类对象调用start方法,就开启了新线程,执行run方法

代码实现:

public class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println("这里是线程任务!");
    }
}

public class Demo {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        new Thread(mt).start();
    }
}

附:匿名内部类方式实现上述两种创建线程的方法

new Thread(){
    @Override
    public void run() {
        for (int i = 1;i < 99; i++){
            System.out.println(Thread.currentThread().getName() + "线程1");
        }
    }
}.start();

new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 1; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+ "线程2");
        }
    }
}).start();

第三种:使用Callable接口

Callable和Runable接口的区别:Callable接口中的call()方法有返回值,声明了异常,而Runable接口中的run()方法没有返回值也没有声明异常。

1、创建Callable对象并且重写call()方法

2、将Callable对象转化为一个可执行的任务

3、创建线程(注意这里的参数为FutureTask类型的可执行任务对象),启动线程

4、获取任务结果

        // 1创建Callable对象
		Callable<Integer> callable = new Callable<Integer>() {
			@Override
			public Integer call() throws Exception {
				System.out.println(Thread.currentThread().getName() + "开始计算");
				int sum = 0;
				for(int i=1; i<=100; i++) {
					sum += i;
				}
				return sum;
			}
		};
		
		// 2把Callable对象转化为可执行任务
		FutureTask<Integer> task = new FutureTask<>(callable);
		
		// 3创建线程
		Thread thread = new Thread(task);
		
		// 4启动线程
		thread.start();
		
		// 5获取任务结果
		Integer result = task.get();
		System.out.println("结果是" + result);

三、实现Runnable接口创建多线程程序的好处

  1. 避免了单继承的局限(一个类只能继承一个类)
  2. 增强了程序的扩展性,降低了程序的耦合性(设置线程任务和开启线程分开了)

 

四、解决线程安全问题的三种方式

1、同步代码块

实现原理:当一个线程抢占cpu资源并执行到代码块时,判断锁对象是否存在,如果存在则锁定代码块并执行,直到执行完毕释放锁对象,当第一个线程没有执行完毕时,这时另一个线程抢占到cpu资源时就获取不到锁对象,无法执行同步代码块,直到前一个线程释放锁对象为止,也就是处于阻塞状态。

实现方式:将可能发生安全问题的代码块放在synchronized关键字中,表示对这个区块中的资源实行互斥访问,其参数为创建的一个任意唯一对象(锁对象)

public class MyThread implements Runnable{
    Object obj = new Object();//创建一个锁对象
    @Override
    public void run() {
        synchronized (obj){
            //这里放可能产生安全问题的代码
        }
    }
}

2、同步方法

实现原理:和同步代码块方式类似,这里的锁对象为this,也就是在启用线程时创建的Runnable对象。

实现方式:将可能产生安全问题的代码封装在同步方法内,再调用该方法即可。

public class MyThread implements Runnable{
    @Override
    public void run() {
        problem();
    }
    public synchronized void problem(){//当为静态方法时,锁对象是本类的class文件对象(反射)
        //这里放可能产生安全问题的代码
    }
}

3、ReentrantLock锁

实现方式:在成员位置创建一个ReentrantLock(重入锁,如同在synchronized中再嵌套一个synchronized代码块)对象,在可能出现安全问题的代码前调用Lock接口中的lock方法获取锁,在可能出现安全问题的代码后调用Lock接口的unlock方法释放锁(一般可能出现安全问题的代码放在try里,释放锁放在finally中)

public class MyThread implements Runnable{
    Lock l = new ReentrantLock();
    @Override
    public void run() {
        l.lock();
        try {
            //这里写可能出现安全问题的代码
        }catch (Exception e){
            e.printStackTrace();    
        }finally {
            l.unlock();
        }
    }
}

五、线程的几种状态

六、线程之间的通信

为什么要处理线程间的通信

多个线程并发执行,在默认情况下CPU是随机切换线程,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行,那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。

如何保证线程间通信有效利用资源

多个线程在处理同一资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一变量的使用或操作。就是多个线程在操作同一份数据时,避免对同一共享变量的争夺。也就是我们需要通过一定的手段使各个线程能有效利用资源,这种手段就是等待唤醒机制。

七、线程池

线程池的原理

线程池就是一个盛放多个线程的容器,【比如一个LinkedList linked,通过Thread t = linked.removeFirst()使用线程和linked.addLast(t)归还线程来操作,所以是队列性质的】当程序第一次启动时就创建多个线程并保存到这个容器中,其中的线程可以反复使用,省去了频繁创建线程对象的操作,从而节省资源。

线程池的创建

1、使用线程池的工厂类Executors的一系列静态方法

不建议使用的弊端:FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE ,可能堆积大量的请求,从而导致OOM。
                                CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。

2、使用构造方法ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

7个参数的含义:

常用线程池的接口和类

https://xiaojin21cen.blog.csdn.net/article/details/87183308

线程池的使用步骤

(一)继承Thread和实现Runable接口方式使用线程池:

  1. 使用线程池的工厂类Executors里的静态方法newFixedTreadPool生产一个指定线程数量的线程池。(返回一个ExecutorService接口的实现类对象)
  2. 创建一个类,实现Runnable接口,重写run方法,设置线程任务。
  3. 调用ExecutorService接口中的submit(Runnable task)方法传递线程任务,开启线程,执行run方法。
  4. 调用ExecutorService接口中的shutdown()方法销毁线程池。[但一般不执行,因为销毁后不能再使用线程池啦,再使用还需重新创建]
//第2步:创建一个类,实现Runnable接口,重写run方法,设置线程任务。
public class MyThread implements Runnable{
    int flag;

    public MyThread(int flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "来执行的任务" + flag);
    }
}



public class Demo {
    public static void main(String[] args) {
        //第1步:使用线程池的工厂类Executors里的静态方法newFixedTreadPool生产一个指定线程数量的线程池。
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        //第3步:调用executorService接口中的submit方法弃用线程
        executorService.submit(new MyThread(1));
        executorService.submit(new MyThread(2));
        executorService.submit(new MyThread(3));
        executorService.submit(new MyThread(4));
        executorService.submit(new MyThread(5));      
        //第4步:关闭线程池(一般不使用)
        executorService.shutdown();
    }
}

(二)当使用Callable接口使用线程池时:

这里的Future表示ExecutorService.submit()返回的结果,实际就是call()方法返回的结果

V get()以阻塞形式等待Future中的异步处理结果( call()的返回值 )

        // 1创建Callable对象
		Callable<Integer> callable = new Callable<Integer>() {
			@Override
			public Integer call() throws Exception {
				System.out.println(Thread.currentThread().getName() + "开始计算");
				int sum = 0;
				for(int i=1; i<=100; i++) {
					sum += i;
				}
				return sum;
			}
		};
		// 2创建线程池
		ExecutorService executorService = Executors.newFixedThreadPool(1);
		// 3调用submit()方法启动线程,并返回Future对象
		Future<Integer> future = executorService.submit(callable);
		// 4获取结果
		Integer result = future.get();		
		System.out.println("结果是" + result);
		// 5关闭线程池
		executorService.shutdown();

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值