简单易懂的多线程初阶

认识线程

进程是系统分配资源的最小单位,线程是系统调度的最小单位。一个进程内的线程之间是可以共享资源的。
每个进程至少有一个线程存在,即主线程

创建多线程的几种方式

第一种方式
创建Thread类的子类
Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类

实现步骤
1.创建一个Thread的子类
2.在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要做什么)
3.创建Thread类的子类对象
4.调用Thread类中的方法,开启新的线程,执行run方法
void start()使该线程开始执行;Java虚拟机调用该线程的run方法
结果是两个线程并发的运行;当前线程(main线程)和另一个线程(创建的新线程,执行其run方法)
多次启动一个线程是非法的。特别是当线程已经结束后,不能再重新启动。
Java程序属于抢占式调度,优先级高的线程优先执行,相同则随机选择一个执行。

第二种方式
实现Runnable接口
Runnable接口应由任何类实现,其实例将由线程执行。 该类必须定义一个无参数的方法,称为run 。

实现步骤:
1.创建一个Runnable接口的实现类
2.在实现类中重写Runnable接口的run方法,设置线程任务
3.创建一个Runnable接口的实现类对象
4.创建Thread类对象,构造方法种传递Runnable接口的实现类对象
5.调用Thread类种的start方法,开启新的线程执行run方法

在这里插入图片描述

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

  1. 避免了单继承的局限性
    一个类只能继承一个类,类继承了Thread类就不能继承其他的类
    实现了Runnable接口,还可以继承其他的类,实现其他的接口

  2. 增强了程序的扩展性,降低了程序的耦合性(解耦)
    实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)

    实现类中,重写了run方法:用来设置线程任务
    创建了Thread类对象,调用start方法:用来开启新线程
    传递不同的实现类,Thread就实现不同的任务

第三种方法
匿名内部类方法实现线程的创建
把子类继承父类,重写父类的方法,创建子类对象一步完成
把实现类实现接口,重写接口中的方法,创建实现类对象合成一步完成
匿名内部类的最终产物:子类/实现类对象,这个类没有名字

格式:
new 父类/接口(){
重复父类/接口中的方法
};

// 使用匿名类创建 Thread 子类对象 
Thread t1 = new Thread() { 
	@Override 
	public void run() { 
		System.out.println("使用匿名类创建 Thread 子类对象"); 
	} 
};
// 使用匿名类创建 Runnable 子类对象 
Thread t2 = new Thread(new Runnable() { 
	@Override 
	public void run() { 
	System.out.println("使用匿名类创建 Runnable 子类对象"); 
	} 
})

Thread的常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable接口创建对象
Thread(String name)创建线程对象并命名
Thread(Runnable target,String name)使用Runnable接口创建对象并命名

线程的状态

在这里插入图片描述

线程安全

如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。

线程不安全的原因
1.原子性
即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
不保证原子性会给多线程带来什么问题
如果一个线程正在对一个变量操作,中途其他线程插入进来了,如果这个操作被打断了,结果就可能是错误的。
2.可见性
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
为了提高效率,JVM在执行过程中,会尽可能的将数据在工作内存中执行,但这样会造成一个问题,共享变量在多线程之间不能及时看到改变,这个就是可见性问题
3.代码顺序性

解决线程安全问题

解决线程安全的三种方法
1.同步。2.静态同步方法 3.锁方法
注意:
1.通过代码块中的锁对象,可以使用任意的对象
2.但是必须保证多个线程使用的锁对象式同一个
3.锁对象的作用:
把同步代码块锁住,只让一个线程在同步代码块中执行

synchronized(锁对象) {
    可能会出现线程安全问题的代码(访问了共享数据的代码)
}

同步技术的原理:
使用了一个锁对象,这个锁对象叫同步锁,也叫对象锁,也叫对象监视器
同步中的线程没有执行完毕不会释放锁,同步外的线程没有锁进不去同步.

同步保证了只能有一个线程在同步中执行共享数据保证了安全,程序频繁的判断锁,获取锁,释放锁程序的效率会降低.

静态的同步方法
锁对象不是this ,this是创建对象之后产生的,静态方法优先于对象
静态方法的锁对象是本类的class属性–>class文件对象(反射)

使用步骤
1.把访问了共享数据的代码抽取出来,放到一个方法中
2.在方法上添加了synchnized修饰符

格式:定义方法的格式:
修饰符 synchnized 返回值类型 方法名(参数列表) {
可能会出现线程安全问题的代码(访问了共享数据的代码)
}
使用lock锁:
使用步骤:
1.在成员位置上创建一个ReentrantLock对象(lock接口实现类)
e.g.Lock l = new ReentrantLock();
2.在可能会出现安全问题的代码前调用Lock接口方法中的方法lock获取锁
3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁

通信-对象的等待集wait set

1.wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)
2.notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。
3.wait(long timeout)让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的notify()方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。

使用wait和notify方法需要注意的细节
1.wait方法和notify方法必须由同一个锁对象调用
2.wait方法与notify方法是属于Object类的方法的,(锁对象可以是任意对象)
3.wait方法与notify方法必须要在同步代码块或者是同步函数中使用(因为要通过锁对象调用这两个方法)

线程池

好处:
1.降低资源消耗
2.提高响应速度。
3.提高线程的可管理性

java.util.concurrent.Executors:线程池的工厂类,用来生成线程池
Executors类中的静态方法:
static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池
参数:int nThreads:创建线程池中包含的线程数量
返回值:
ExecutorService接口,返回的是ExecutorService接口的实现类对象我们可以使用ExecutorService接口来接受(面向接口编程)
线程池的使用步骤:
1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法;
4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行)

对比线程与进程

1 线程的优点

  1. 创建一个新线程的代价要比创建一个新进程小得多
  2. 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  3. 线程占用的资源要比进程少很多
  4. 能充分利用多处理器的可并行数量
  5. 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
  6. 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
  7. I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作

2 进程与线程的区别

  1. 进程是系统进行资源分配和调度的一个独立单位,线程是程序执行的最小单位。
  2. 进程有自己的内存地址空间,线程只独享指令流执行的必要资源,如寄存器和栈。
  3. 由于同一进程的各线程间共享内存和文件资源,可以不通过内核进行直接通信。
  4. 线程的创建、切换及终止效率更高。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值