多线程
基础概念
- 程序(program):一段静态代码,一组指令的集合
- 进程(process):程序的一次执行过程,或是一个正在运行的程序,资源分配的单位
- 线程(thread):一个进程可以有多个线程,是独立调度和分派的基本单位
- 线程共享方法区和堆,stack和program couter Register(程序计数器)单独
- 并行:多个cpu同时执行多个任务
- 并发:一个cpu采用时间片轮转法执行多个任务
- 多线程优点:提高程序响应性,提高cpu利用率,改善程序结构(单核cpu多线程执行有可能速度反而变慢)
什么时候需要多线程
-
需要同时执行两个货多个任务
-
程序需要实现一些要等待的任务
-
需要一些后台运行程序
实现多线程方式
继承Thread类
- 不可以直接在主线程调用run方法,这样并没有启动新的线程
- 同一个子类线程对象只能执行一次start()方法,否则会抛出异常,
- 要多个线程只能创建多个对象
if (threadStatus != 0)
throw new IllegalThreadStateException();
/**
* @description:
* @author: liSen
* @time: 2021/6/4 19:40
*/
public class ThreadTest {
public static void main(String[] args) {
//3.创建子类对象执行Start方法
new MyThread().start();//start()方法执行步骤1.启动当前的线程2.执行run方法
for (int i = 0; i <100; i++) {
System.out.println(Thread.currentThread().getName());
System.out.println("主线程");
}
//匿名子类
new Thread() {
@Override
public void run() {
System.out.println("这是子线程");
}
}.start();
}
}
//1.创建继承Thread子类
class MyThread extends Thread{
//2.重写run方法
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName());
System.out.println("子线程执行");
}
}
}
实现Runnable接口
package com.company.Thread;
/**
* @description:
* @author: liSen
* @time: 2021/6/4 22:07
*/
public class ThreadTest2 {
public static void main(String[] args) {
//3.创建实现类对象
MThread mThread = new MThread();
//4.创建Thread对象
Thread thread = new Thread(mThread);
//5.调用start方法
thread.start();
}
}
//1.创建一个时间Runnable接口的类
class MThread implements Runnable{
//2.生成run方法
@Override
public void run() {
System.out.println("子线程1 ");
}
}
比较实现Runnable更好
- 没有单继承限制
- 能解决多线程数据共享问题
实现Callable接口
/**
* @description: 使用Callable实现多线程,jdk5.0
* @author: liSen
* @time: 2021/6/9 16:13
*/
public class ThreadNew {
public static void main(String[] args) {
//3.生成sum实例
Sum sum = new Sum();
//4.生成FutureTask实例
FutureTask<Integer> futureTask = new FutureTask(sum);
//5.new 一个新的线程
new Thread(futureTask).start();
Integer o = null;
//6.可以获取call的返回值
try {
o = futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(o);
}
}
//1。实现Callable接口(可以使用泛型)
class Sum implements Callable<Integer>{
private Integer sum = 0;
//2.实现call方法(可以有返回值)
@Override
public Integer call() throws Exception {
for (int i = 0; i < 100; i++) {
if(i % 2 == 0){
sum +=i;
System.out.println(i);
}
}
return sum;
}
}
比Runnable好
- 可以使用泛型
- call可以有返回值
- 可以抛出异常被捕获
线程池
方法
-
String getName()获取某个线程的名字
Thread.currentThread().getName()//获取当前线程的名字 Thread thread1 = new MyThread() thrad1.getName();//获取thread1线程的名称
-
void start() 启动线程,并执行线程的run方法
new Mythread().start();
-
run()线程在被调度时的执行操作
-
void setName() 设置该线程的名称
1.给主线成命名 Thread.currentThread().setName("123"); 2.线程子类实现构造器, //Thread类自带的命名构造器 public Thread(String name) { init(null, null, name, 0); } //在自己子类实现就好 public MyThread(String threadName){ super(threadName); }
-
static Thread currentThread() 返回当先线程,在子类中就是this,通常在主线下和Runnable实现
-
yield()释放当前cpu的执行权,重新分配执行线程,可能被一个线程连续多次抢到执行权
//1.创建继承Thread子类 class MyThread extends Thread{ //2.重写run方法 @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()); System.out.println("子线程执行"); if(i % 5 == 0){ yield(); } } } /* public MyThread(String threadName){ super(threadName); }*/ }
-
join()在线程A调用线程B的join(),线程A进入阻塞状态,直到线程B执行完后,线程A才结束阻塞
-
stop()已过时,强制结束线程生命
-
sleep(long millitme)让当前线程数目指定毫秒,指定时间内线程时阻塞的
-
isAlive()判断线程是否存在
-
setPriority/getPriority()设置/获取线程的优先级,高优先级抢占只是概率问题(线程的优先权高度依赖于系统,例如win有七个优先权,java的优先级映射到操作系统的优先级中,但是在Oracle未linux提供的ava虚拟机会忽略线程优先级—所有线程优先级相同。现在不推荐使用优先级)
-
Thread.State getState()获取当前线程的状态,NEW、RUNNABLE…
线程调度策略
- 调度策略:时间片,特点:抢占式(高优先级的线程抢占cpu)
- Java调度方式:
- 同级线程组成先进先出的队列(先到先服务),使用时间片策略
- 对高优先级,室友优先级的抢占策略
- 线程的优先级:
- MAX_PRIORITY =10
- MIN_PRIORITY = 1
- NORM_PRIORITY = 5
- 设置优先级:
- setPriority()
- getPriority()
线程安全问题
- 多个线程方位同一个变量如买票时票的总数,如果使用extends Thread实现多线程,应将该变量设置为static,多个线程访问同一份变量,Runnble是实现是因为只有子类一份实现类,所以无需使用。
线程的生命周期
public enum State {
NEW,//新建:new Thewad(r),线程刚创建还未启动
RUNNABLE,//可运行的,调用start()方法就是处于可运行状态,因为操作系统采用的是抢占式的调度,时间片用完剥夺运行权给其他线程,所以是线程是在运行,也可能没在运行
BLOCKED,//阻塞:当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态
WAITING,//等待:一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒
TIMED_WAITING,//计时等待:同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态,这一状态将一直保持到超时期满或者接收到唤醒通知,带有超时参数的常用方法有Thread.sleep、锁对象.wait()
TERMINATED;//终止:因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡
}
- 新建(new):线程被创建时
- 就绪:新建线程被start()后,线程进入线程队列等待cpu时间片,此时已经具备运行条件,只是没有分配资源
- 运行:就绪线程被调度获得cpu资源,进入运行状态,run()定义了线程的操作和功能
- 阻塞:在某种特殊情况下,被人为挂起或者执行输入输出操作时,让出cpu并临时终止自己的执行,进入阻塞
- 死亡:线程完成了他的全部工作或者线程被提前强制性终止或异常导致结束
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0uh2WU03-1623719406840)(D:\Document\note\java重新开始\基础\pic\thread.jpg)]
线程同步
同步代码块
//1.创建一个时间Runnable接口的类
class MThread implements Runnable{
private Integer t =100;
Object obj = new Object();//多个线程必须用同意把锁
//2.生成run方法
@Override
public void run() {
whlie(true){
synchronized(obj){//同步监视器:锁,任何一个对象,类都可以当锁
//synchronized(this){//同步监视器:锁,任何一个对象,类都可以当锁
synchronized(MThread.class){//类只会加载一此所以可以当锁
//被同步的代码(操作共享数据的代码)
//共享数据:多个线程操作的数据
t--;
System.out.println(t);
}
}
System.out.println("子线程1 ");
}
}
//1.创建继承Thread子类
class MyThread extends Thread{
private static Integer t =100;
//2.重写run方法
@Override
public void run() {
while (true){
synchronized(MyThread.class){//保证锁的唯一性(继承不能用this,new 对象,因为会有多个实例)
t--;
}
System.out.println(Thread.currentThread().getName());
System.out.println("子线程执行");
}
}
}
被同步的代码块相当于单线程
同步方法
//1.创建继承Thread子类
class MyThread extends Thread{
private static Integer t =100;//static多个对象访问同一个元素
@Override
public void run() {
while(true){
show();
}
}
public static synchronized void show(){//加上static 同步锁是类本身即MyThread.class 反之为this即生成对象(失败)
if(t > 0){
t--;
System.out.println(t);
}
}
}
//1.创建一个时间Runnable接口的类
class MThread implements Runnable{
private Integer t =100;
@Override
public void run() {
while(true){
show();
}
}
public synchronized void show(){//默认是this,继承Runnable只有一个实例对象(可以)
if(t > 0){
t--;
System.out.println(t);
}
}
}
Lock(锁)
class Window implements Runnable{
private Integer t = 100;
private ReentrantLock rt = new ReentrantLock(true);//使用可重用锁 (fair:ture,线程按照先进先出的结构使用资源,而非抢占,默认fair:false)
@Override
public void run() {
while (true){
try{
rt.lock();//添加锁
if(t>0){
t--;
System.out.println(Thread.currentThread().getName()+"----------"+t);
}
}finally {
rt.unlock();//解除锁
}
}
}
}
synchronized与Lock
- 推荐使用顺序:Lock—同步代码块—同步方法
- 不同:Lock需要手动添加同步锁(lock),手动释放同步锁(unlock)
同步锁解决懒汉式线程安全问题
//同步方法
class Person{
private static Person p1 = null;
private Person(){//私有不让在外部new对象
System.out.println("对象");
}
public static synchronized Person getPerson(){//同步方法,默认指向this
if(p1 == null){
p1 = new Person();
}
return p1;
}
}
//同步代码块
class Person{
private static Person p1 = null;
private Person(){
System.out.println("对象");
}
public static Person getPerson(){
if(p1 == null){//防止已经生曾对象,多个对象还在等待进去
synchronized(Person.class){
if(p1 == null){//有多个对象在排队到同步区域,判断是否已经存在
p1 = new Person();
}
}
}
return p1;
}
}
死锁
- 不同的线程分别占用对方需要同步的资源不放弃,都在等对方放弃自己同步的资源,造成死锁
线程通信
class Number implements Runnable{
private Integer t = 100;
Object obj = new Object();
@Override
public void run() {
while (true){
// synchronized(this){
synchronized(obj){
//notify(); //默认是this,当锁不是this时候因为两个不同会报异常 (java.lang.IllegalMonitorStateException)
obj.notify(); //方法继承于object,指向和锁相同即可
if(t>0){
System.out.println(Thread.currentThread().getName()+"-------------"+t);
t--;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
方法
- **wait()😗*等待,一但执行此方法线程进入组赛状态,释放同步监视器
- **notify()😗*执行此方法,有一个线程被唤醒,有多个线程的时候根据优先级唤醒一个
- notifyAll():执行此方法,唤醒所有等待的线程
说明
- 三个方法必须在同步代码块中使用
- 三个方法必须与同步锁指向同一个对象,否则出现异常
- 三个方法继承自Object对象
wait()和sleep()异同
相同:
- 两者都可以使得线程进入阻塞状态
不同:
- sleep()属于Thread()方法,wait()属于Object()
- sleep()不依赖与同步代码监视器synchronized,wait()依赖与synchronized
- sleep()不会释放lock,wait()会释放lock
- sleep()不用手动唤醒(到时间自动唤醒),wait()需要手动唤醒
生产者消费者问题
package com.company.Thread;
/**
* @description:
* @author: liSen
* @time: 2021/6/9 15:42
*/
public class ProductTest{
public static void main(String[] args) {
Clerk clerk = new Clerk();
Product product = new Product(clerk);
Consumer consumer = new Consumer(clerk);
product.setName("生产者1");
consumer.setName("消费者1");
product.start();
consumer.start();
}
}
class Product extends Thread{
private Clerk clerk = null;
public Product(Clerk clerk) {
this.clerk = clerk;
System.out.println("开始生产");
}
@Override
public void run() {
while (true){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.addProductCount();
}
}
}
class Consumer extends Thread{
private Clerk clerk = null;
public Consumer(Clerk clerk) {
this.clerk = clerk;
System.out.println("开始消费");
}
@Override
public void run() {
while (true){
try {
sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.subStractProductCount();
}
}
}
class Clerk{
private int productCount =0;
public synchronized void addProductCount(){
if(productCount < 20){
productCount++;
System.out.println(Thread.currentThread().getName()+"---------"+"生产:"+productCount);
notify();//生产好就唤醒
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void subStractProductCount(){
if(productCount > 0){
System.out.println(Thread.currentThread().getName()+"---------"+"消费:"+productCount);
productCount--;
notify();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}