多线程

Java多线程

概念:

1.程序:

​ 静止的,是计算机指令的集合,它以文件的形式存储在磁盘上,只有当程序获得cpu资源运行起来,才称为进程,就好比我们下载一个app然后把它放在了本地的磁盘

2.进程:

由多个线程组成,相互协作完成指定的作业.运行起来的程序就是进程,cpu分配资源的单位.下载后我们使用这款app,点开它,获得了cpu资源,运行起来就是一个进程

在这里插入图片描述

3.线程:

​ 线程又称为轻量级的进程,cpu调度资源的最小单位,就好比一个公司是一个程序,下边的每个部门就是一个进程,公司按部门分配资源,分配资源后,怎么用,谁用,所以部门中的每一个人就是一个线程,他们是调度使用资源的最小单位.

在这里插入图片描述

4.单线程:

​ 如果一个进程只有一个线程,这样的程序称为单线程程序,一个部门就你一个人,没人和你抢资源,但是这样太浪费资源

5.多线程:

​ 如果一个进程拥有不止一个线程,那么这个进程就是多线程的.优势:可以同时执行多个任务。提高运行的效率。

线程的基本组成部分:

​ 1.cpu时间片,操作系统会为每个线程分配执行时间

​ 2.运行数据:

​ 堆空间:存储线程需要的对象,(多个线程可以共享堆中的对对对象)

​ 栈空间:存储线程需要的局部变量(每个线程都有独立的栈,程序计数器)

​ 3.线程的逻辑代码

线程的使用场景:

1.比如app开发中耗时的操作都不在UI主线程中做

2.实现响应更快的应用程序, 即主线程专门监听用户请求,子线程用来处理用户请求。以获得大的吞吐量。
感觉这种情况下,多线程的效率未必高。 这种情况下的多线程是为了不必等待, 可以并行处理多条数据。

比如JavaWeb的就是主线程专门监听用户的HTTP请求,然后启动子线程去处理用户的HTTP请求。

3.某种任务,虽然耗时,但是不耗CPU的操作时,开启多个线程,效率会有显著提高。
比如读取文件,然后处理。 磁盘IO是个很耗费时间,但是不耗CPU计算的工作。 所以可以一个线程读取数据,一个线程处理数据。肯定比一个线程读取数据,然后处理效率高。 因为两个线程的时候充分利用了CPU等待磁盘IO的空闲时间。

创建线程:

1.创建一个类继承Thread然后重写run()方法

线程A:

public class MyThread extends Thread {	
	@Override
	public void run() {
		for(int i = 0; i < 500; i++) {
			System.out.println("线程A: "+ i);
		}
	}
}

线程B:

public class MyThread2 extends Thread {
	@Override
	public void run() {
		for(int i = 0; i < 500; i++) {
			System.out.println("线程B: " + i);
		}
	}
}

测试:

public class Test {
    @Test
	public void testExetendThread() {
		MyThread myThread = new MyThread();
		myThread.start();
		MyThread2 myThread2 = new MyThread2();
		myThread2.start();	
	}
}

结果:
在这里插入图片描述

2.事项Runable接口,然后重写run方法

创建线程:

​ 线程A

public class MyThread3 implements Runnable {
	@Override
	public void run() {
		for (int i = 0; i < 500; i++) {
			System.out.println("线程A: " + i);
		}
	}
}

​ 线程B

public class MyThread4 implements Runnable {
	@Override
	public void run() {
		for (int i = 0; i < 500; i++) {
			System.out.println("线程B: " + i);
		}
	}
}

测试

public class Test {
    @Test
	public void testImplRnable() {
		Runnable runnable3 = new MyThread3();
		Thread myThread3 = new Thread(runnable3);
		myThread3.start();
		
		Runnable runnable4 = new MyThread4();
		Thread myThread4 = new Thread(runnable4);
		myThread4.start();
	}
}

结果

在这里插入图片描述

3.匿名内部类

两种写法:第一种

public class Test {
    @Test
	public void testRunableImpl() {
		Runnable mr = ()-> {
				for (int i = 0; i < 500; i++) {
					System.out.println("线程A: " + i);
				}
		
		};
		Thread t = new Thread(mr);
		t.start();
	}
}

或者这样写:

public class Test {
    @Test
	public void testAisThread() {
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 500; i++) {
					System.out.println(Thread.currentThread().getName()+": " + i);
				}
			}
		},"线程A").start();
	}
}

第二种:

public class TestMultiplyThread2 {
	@Test
	public void TestThread() {
		Thread t1 = new Thread("线程A") {
			@Override
			public void run() {
				System.out.println("我是一个子线程");
				for(int i = 0; i < 500; i++) {
					System.out.println(i);
				}
			}
		};
		t1.start();
		
		System.out.println("hello world");
	}

}

结果

在这里插入图片描述

4.Lamda表达式:

public class Test {
	 @Test
	public void testAisThread() {
		new Thread(() -> {
			for (int i = 0; i < 500; i++) {
				System.out.println(Thread.currentThread().getName() + ": " + i);
			}
		}, "线程A").start();
	}
}

需要注意的是使用lamda表达式必须是函数式接口(即接口中只有一个方法).

5.Calllable接口和Future

这个需要配合线程池来使用,这里举一个简单的例子吧

public class Test {
	public void testFutureThread(String[] args) {
		//这里面的核心线程数和最大线程数是一样的10
		ExecutorService executorService = Executors.newFixedThreadPool(10);
		Future<String> future = executorService.submit(new Callable<String>() {
			@Override
			public String call() throws Exception {
				
				return "学会了吗?";
			}
		});
		try {
			System.out.println(future.get());
		} catch (InterruptedException | ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

newFixedThreadPool底层:

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

结果:
在这里插入图片描述

多线程的常用方法

currentThread() 获取当前线程对象。 类方法

setName(String name) 设置线程的名字。

getName() 获取线程的名字。

setPriority(int priority) 设置线程的优先级。 优先级的取值范围[1,10],默认是5

getPriority() 获取线程的优先级。

getState() 获取线程的状态

join() 执行该线程,会阻塞当前线程。

sleep(long millis) 休眠指定时间(单位毫秒),会阻塞当前线程。类方法

start() 启动线程

yield() 暂定该线程的执行,交出CPU的使用权。

线程的同步

线程的同步和线程的并发

​ 1:线程的同步不是客观上的线程从宏观上的同步运行,微观上的串行的过程这一现象,而恰恰相反,线程的同步是指的线程的不同时执行,而是串行,以保证线程对临界资源的访问.

​ 2:线程的并发,指的是线程从宏观上一起运行,微观上还是串行的,因为那个线程获得cpu的时间片,谁就运行.

卖票的实例:

​ 不同步发生的数据混乱现像,即多个线程对同一临界资源的访问打破了原理操作的情况下:

简单的java代码:

public class Tickets {
	private int tickets = 100;
	public int getTickets() {
		return tickets;
	}	
	public void sellTickets() {
		tickets--;
		System.out.println(Thread.currentThread().getName() + " : 买了一张票,当前剩余: 			" +  tickets+ "张票");
	}
}

测试代码

public class TMyTest {
    @Test
	public void testStickets() {
		Tickets t1 = new Tickets();
		new Thread(() -> {
			while (true) {
				if (t1.getTickets() > 1) {
					t1.sellTickets();	
				}else {
					return;
				}	
			}
		}, "线程A").start();

		new Thread(() -> {
			while (true) {
				if (t1.getTickets() > 1) {
					t1.sellTickets();
					
				}else {
					return;
				}	
			}
		}, "线程B").start();
	}
}

结果出现不符合现实的现像:即500张票卖出了1000张,出现数据的混乱,如下:

在这里插入图片描述

线程的同步操作

同步方法:
//在返回值前面加synchronized,语法是
public 方法修饰符 synchronized 返回值类型 方法名(参数){}


public class Tickets {
	private int tickets = 100;	
	public int getTickets() {
		return tickets;
	}	
	public synchronized void sellTickets() {
		tickets--;
		System.out.println(Thread.currentThread().getName() + " : 买了一张票,当前剩余: 			" +  tickets+ "张票");
	}
}

测试代码

public class TMyTest {
    @Test
	public void testStickets() {
		Tickets t1 = new Tickets();
		new Thread(() -> {
			while (true) {
				if (t1.getTickets() > 1) {
					t1.sellTickets();	
				}else {
					return;
				}	
			}
		}, "线程A").start();

		new Thread(() -> {
			while (true) {
				if (t1.getTickets() > 1) {
					t1.sellTickets();
					
				}else {
					return;
				}	
			}
		}, "线程B").start();
	}
}

结果:

在这里插入图片描述

同步代码块
public class Tickets {
	private int tickets = 100;	
	public int getTickets() {
		return tickets;
	}	
	public void sellTickets() {
        //同步代码块:
		synchronized(this) {
			tickets--;
			System.out.println(Thread.currentThread().getName() + " : 买了一张票,当前剩				余: " +  tickets+ "张票");
		}	
	}
}
重入锁(ReentrantLook):
public class Tickets {
	private int tickets = 100;

	public int getTickets() {
		return tickets;
	}
	public void sellTickets() {
        //在juc包下,重入锁
		Lock lock = new ReentrantLock();
		lock.lock();
		tickets--;
		System.out.println(Thread.currentThread().getName() + " : 买了一张票,当前剩余: " + tickets + "张票");
		lock.unlock();
	}
}

线程间的通信

什么是线程通信?

不同线程之间可以相互的发信号。这就是线程通信。之所以需要进行线程通信,是因为有些时候,一个线程的执行需要依赖另外一个线程的执行结果。在结果到来之前,让线程等待(wait),有了结果只之后再进行后续的操作。对于另外一个线程而言,计算完结果,通知(notify)一下处于等待状态的线程.

线程通信借助的是Object类的wait,notify,nitifyall方法。

wait作用是让当前线程阻塞,阻塞多久,取决于有没有其他线程唤醒它。

notify作用是唤醒处于wait状态的线程。必须是同一个监视器下的线程。

notifyall作用是唤醒所有处于wait状态的线程。必须是同一个监视器下的线程。

一般情况下,多线程里会出现线程同步的问题,我们不但要进行线程通信,还要解决线程同步的问题。

生产者-消费者模式

这是一个比较经典的多线程场景。有商品的时候,消费者才可以消费,没有商品的时候,消费者等待。商品库存充足的时候,生产者等待,库存不满的时候,生产者生产商品。

public class Saler {//售货员类
	private int productCount = 10;	//商品数量
	public synchronized void stockGoods() {
		if(productCount < 2000) {
			productCount++;
			System.out.println(Thread.currentThread().getName() + "生产了1件商品,库存是:" + productCount);
			this.notifyAll();
		}else {
			System.out.println("库存满了");
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	public synchronized void sellGoods() {
		if(productCount > 0) {
			productCount--;
			System.out.println(Thread.currentThread().getName() + "购买了1件商品,库存剩余:" + productCount);
			 
			this.notifyAll();
		}else {
			System.out.println("库存不足");
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

public class Productor implements Runnable{//生产者类
	private Saler s;

	public Productor(Saler s) {
		super();
		this.s = s;
	}

	@Override
	public void run() {
		while(true) {
			s.stockGoods();
		}
	}
	
}
public class Customer implements Runnable{//消费者类

	private Saler s;
	
	
	public Customer(Saler s) {
		super();
		this.s = s;
	}


	@Override
	public void run() {
		while(true) {
			s.sellGoods();
		}
	}
	
}
public class TestTread {

	public static void main(String[] args) {
		//生产者-消费者模式。模拟生产和消费过程
		Saler s = new Saler();
		Customer c = new Customer(s);
		Productor p = new Productor(s);
		Thread t1 = new Thread(c, "客户1");
		t1.start();
		Thread t2 = new Thread(p,"厂家");
		t2.start();
		Customer c2 = new Customer(s);
		Thread t3 = new Thread(c2, "客户2");
		t3.start();
	}

}

注意:调用notify()方法必须在对该对象加锁的同步代码块中

线程的生命周期

API中的java.lang包下的Thread.State枚举类下定义的线程状态如下:

NEW:新建状态,指的是线程已经创建,但是尚未start()。

RUNNABLE:可运行状态(已经调用了start方法),已经准备就绪,一旦抢到CPU就立即执行。

BLOCKED:阻塞状态,处于阻塞状态的线程正在等待进入Synchronized块(或方法)。

WAITING:等待状态,等待其他线程执行任务。直到其他线程任务结束或者收到notify信号。

TIMED-WAITING:等待状态,限定时间的等待状态。

TERMINATED:终止状态。线程要运行的任务已经结束。

在这里插入图片描述

线程池

线程池的相关类树

在这里插入图片描述

ThreadPoolExecutor底层构造方法:

   public ThreadPoolExecutor(
       					//核心线程数
       						int corePoolSize,
       					//最大线程数
                            int maximumPoolSize,
       					//除了核心线程外的线程的最大存活时间
                              long keepAliveTime,
       					//时间的单位是TimeUnit是一个enmu(enmunation:枚举)类,
                              TimeUnit unit,
       					//任务队列
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

手动创建一个线性池

public static void main(String[] args) {
		ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 20, 5, TimeUnit.MINUTES, new ArrayBlockingQueue(3));
		pool.execute(new Runnable() {//实现Executor接口中得execute方法
			@Override
			public void run() {
				System.out.println("你好世界!");
			}	
		});
		
		pool.submit(new Runnable() {//实现接口Executor子接口ExecutorService中的submit方法
			@Override
			public void run() {
				System.out.println("你是小猪!");
			}
		});
	}
}

对ExecutorService,Executor和Executors工具类创建线性池与Spring使用一下

applicationContext.xml中的配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
        ">
    <context:component-scan base-package="com.lanou.dao" />
    <context:property-placeholder location="jdbc.properties" />
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${jdbc.user}" />
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" c:ds-ref="dataSource" />

</beans>

jdbc.properties文件的配置:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/day05?characterEncoding=utf-8&useSSL=false
jdbc.user=root
jdbc.password=123456

bean中的实体类:

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Setter
@Getter
@ToString
public class Girl {
    private int gId;
    private String gName;
    private int kId;
}

测试方法:与Callable和Future接口的使用.

    @Test
    public void testExecutors(){

        ExecutorService es = Executors.newFixedThreadPool(10);
        Future<List<Girl>> future = es.submit(new Callable<List<Girl>>() {
            @Override
            public List<Girl> call() throws Exception {
                ApplicationContext applicationContext = new 	               ClassPathXmlApplicationContext("applicationContext.xml");
                QueryRunner queryRunner = applicationContext.getBean(QueryRunner.class);
                String sql = "select g_id gId,g_name gName,k_id kId from girl";
                List<Girl> girlList = queryRunner.query(sql, new BeanListHandler<Girl>(Girl.class));
                return girlList;  
            }
        });
        
        try {
            for(Girl girl : future.get()){
                System.out.println(girl.toString());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

测试结果:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值