多线程的创建及使用(Thread和Runnable)——java基础

谈多线程之前,先来了解一些概念:

单核和多核

学过操作系统我们都知道,操作系统有单处理机系统和多处理机系统之分。单处理机也就只有一核处理器CPU,多处理机也就有多核处理器了,所以它的计算处理性能肯定是强于单核系统的。

并行与并发

并行就是同一时间处理多条命令,就是一个时间点可以处理多个任务;并发就是同一时间段处理多条命令,虽然微观上分时交替执行,但之间的间隙人们感觉不到,宏观上还是多个命令同时执行。可见,多核系统可以做到程序的并行执行,而单核不可以。

进程与线程

进程就是一个内存中运行的应用程序,它是操作系统资源分配的基本单位。我们一个应用程序可以有多个进程在同时运行。线程是进程的一个实例,是CPU进行资源调度的基本单位,一个进程可以有多个线程在同时运行。一个程序至少有一个进程,一个进程至少有一个线程。

1,多线程实现方式

java中实现多线程的两种方式:

  • 方法1,继承Thread类,重写run方法;
public class ThreadTest {
    public static void main(String[] args) {
        MyThread myThread1 = new MyThread("mythread1");
        MyThread myThread2 = new MyThread("mythread2");
        myThread1.start();
        myThread2.start();
        for(int i = 0;i < 2;i++){
            System.out.println("main线程输出"+i);
        }
    }
}

/**
 * 自定义多线程类,重写run方法
 */
class MyThread extends Thread{
    public MyThread(String name) {
        super(name);
    }

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

//某一次输出结果
main线程输出0
mythread2 0
mythread2 1
mythread1 0
mythread1 1
main线程输出1
  • 方法2,实现runnable接口,重写run方法,并通过构建Thread类,将runnable作为目标对象构建多线程;
	 MyRunnable myRunnable1 = new MyRunnable();
     Thread thread1 = new Thread(myRunnable1,"myRunnable1");
     Thread thread2 = new Thread(myRunnable1,"myRunnable2");
     thread1.start();
     thread2.start();
        for(int i = 0;i < 2;i++){
            System.out.println("main线程输出"+i);
        }
    }

class MyRunnable implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i < 2;i++){
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
}
//某一次输出结果
main线程输出0
myRunnable1 0
main线程输出1
myRunnable2 0
myRunnable1 1
myRunnable2 1

法2匿名内部类方式构建多线程:

  Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类方式");
            }
        };
        new Thread(runnable).start();

2,线程安全问题的解决方式

多线程自然就会造成线程安全问题,下面介绍三种线程安全解决机制:

  1. synchronized(){}同步代码块;
  2. synchronized同步方法;
  3. 锁机制。
//传入任意对象object
synchronized (object) {
	需要同步执行的代码
}
public synchronized void method(){ 
	需要同步执行的代码
}
Lock lock = new ReentrantLock();
	lock.lock();
		需要同步执行的代码
	lock.unlock();

3,线程的几种状态

在API中,通过java.lang.Thread.State枚举出了六种状态,他们分别是:

image-20210127100049990

线程间的状态切换:

image-20210127101352608

tips:一个时间只能有一个线程获得对象锁,如果某个线程进入等待wait状态,那么它将释放锁;休眠sleep状态则不会释放锁。

wait()、notify()、notifyAll()方法是Object对象所有,所以他们必须是在synchronized内调用;而sleep()方法通过Thread类调用即可。

import java.util.Date;
public class ThreadTest2 {
    public static void main(String[] args) {
        Object object = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println(new Date() + "-" + Thread.currentThread().getName() + "得到执行并获取到当前锁,调用wait方法,进入waitting状态");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(new Date() + "-" + Thread.currentThread().getName() + "被唤醒");
                }
            }
        }, "线程1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println(new Date() + "-" + Thread.currentThread().getName() + "休眠5s,此时不会释放锁");
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                                       System.out.println(new Date() + "-" + Thread.currentThread().getName() + "苏醒后设置等待2s,此时会释放锁");
                    try {
                        object.wait(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(new Date() + "-" + Thread.currentThread().getName() + "等待完毕立即获得锁,执行唤醒waitting中的线程1");
                    object.notify();
                }
            }
        }, "线程2").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (object) {
                        System.out.println(new Date() + "-" + Thread.currentThread().getName() + "获取锁对象执行了");
                    }
                }
            }
        }, "线程3").start();
    }
}
//某次执行结果:
Wed Jan 27 16:56:39 CST 2021-线程1得到执行并获取到当前锁,调用wait方法,进入waitting状态
Wed Jan 27 16:56:39 CST 2021-线程2休眠5s,此时不会释放锁
Wed Jan 27 16:56:44 CST 2021-线程2苏醒后设置等待2s,此时会释放锁
Wed Jan 27 16:56:44 CST 2021-线程3获取锁对象执行了
Wed Jan 27 16:56:45 CST 2021-线程3获取锁对象执行了
Wed Jan 27 16:56:46 CST 2021-线程2等待完毕立即获得锁,执行唤醒waitting中的线程1
Wed Jan 27 16:56:46 CST 2021-线程1被唤醒
Wed Jan 27 16:56:46 CST 2021-线程3获取锁对象执行了
Wed Jan 27 16:56:47 CST 2021-线程3获取锁对象执行了

可以看到,开始线程1抢到资源获得执行进入无线等待状态,然后线程2获得执行进入休眠5s,此时并不会释放对象锁,线程3无法执行,所以系统5s内处于空闲状态(这里3个线程都加了对象锁,如果某个没有则无此限制)。5s后线程2苏醒,执行2s等待,此时会释放锁,那么系统里的线程3得到机会便立即执行,直到线程2再次苏醒并唤起线程1。线程1执行完后,就是并发执行了,但此时系统只有线程3会一直循环执行下去。

4,线程池

线程的创建非常的简单,但线程同样需要管理。我们根据事务的并发量来确定线程的创建多少。但大量的创建线程务必会影响资源的开销,如何做好线程的回收?如何有效的对线程进行管理?为此,我们有线程池的解决机制。线程池就是一个容纳多个线程的容器,使用就向池里面获取,使用完毕后进行回收。一次创建,多次(终身)使用,这样就无需反复创建线程了。

线程池的好处:

  1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
  2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

线程池的创建:

 public static void main(String[] args) {
     	//创建线程池对象
        ExecutorService executorService = Executors.newFixedThreadPool(2);
     	//创建Runnable实例对象
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"获得了线程池资源");
            }
        };
     	//从线程池中获取线程对象,调用Runnable中的run()方法
        executorService.submit(runnable);
        executorService.submit(runnable);
        executorService.submit(runnable);
     	//关闭线程池
        //executorService.shutdown();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值