文章目录
多线程
线程是操作系统能够运算调度的最小单位,被包含在进程之中
就是让程序做多件事情
并发和并行
并发,在同一时刻,有多个指令在单个CPU上交替 执行
并行,在同一时刻,多个指令在多个CPU上同时执行
多线程的实现方式
Thread类实现
public class MyThread extends Thread{
@Override
public void run(){
for(int i= 0;i<100;i++){
System.out.println(getName()+"hello");
}
}
}
//自己定义一个类继承thread
//重写run方法
//创建子类的对象,启动线程
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
Runnable接口方式实现
接口的意义,java是单继承方式的,所以为了扩展性和灵活性有个接口
public class MyRun implements Runnable{
@Override
public void run() {
for(int i= 0;i<100;i++){
//调用当前线程的静态方法,获取当前线程对象
Thread t = Thread.currentThread();
System.out.println(t.getName()+"hello");
//或者用链式编程写成以下
System.out.println(Thread.currentThread().getName()+"hello");
}
}
}
public static void main(String[] args) {
MyRun mr = new MyRun();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
callable接口和Future接口实现
public class Mycallable implements Callable<Integer> {
//前面两种方式不能返回线程的结果,但这个可以
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum = sum+i;
}
return sum;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Mycallable mc = new Mycallable();
//用FutureTask对象管理运行结果
FutureTask<Integer> ft = new FutureTask<>(mc);
Thread t1= new Thread();
t1.start();
Integer result = ft.get();
System.out.println(result);
}
常用的成员方法
getName()//返回线程的名称,在main方法中不指定是那个线程,这个线程的名字就是main线程,跟linux系统编程一样,同属一个祖宗
setName()//设置线程的名字
static Thread currentThread()//获取当前线程的对象
static void sleep();设置休眠时间
//优先级方法,抢占式调度,优先级越高,抢到cpu概率是越大的,只是概率大
setPriority()//设置线程的优先级
getPriority()//获得线程优先级
//守护线程,当其他的非守护线程执行完毕,守护线程会陆续结束,没有存在的必要了
//比如聊天窗口线程结束,那么传输文件的线程也就没必要存在了
setDaemon()//设置为守护线程
//礼让线程
yield()//出让当前CPU的执行权
//插入线程
t.join()//把t线程插入到当前线程之前
线程的生命周期
- 新建,创建线程
- 就绪,有执行资格没有执行权
- 运行,有执行资格有执行权
- 阻塞,没有执行资格没有执行权
- 死亡,线程死亡变成垃圾
线程的安全问题
线程执行时,有随机性
同步代码块
锁就是大门钥匙,谁拿到了,谁就可以进到对应的程序里面
//三个窗口买票,共卖一百张票
public class MyThread extends Thread{
static int ticket = 0;
static Object obj = new Object();
public void run(){
while(true){
//同步代码块
//锁,就类似于sql的事务
synchronized (obj){
if(ticket<100){
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
ticket++;
System.out.println(getName()+"正在卖第"+ticket+"张票");
}
}
}
}
}
同步方法
private synchronized boolean extracted()
-
同步方法是锁方法里面所有的代码
-
锁对象不能自己指定,如果是非静态方法,this,如果是静态方法是当前的字节码文件
public class MyRun implements Runnable{
int ticket = 0;
@Override
public void run() {
while(true){
if (extracted()) break;
}
}
private synchronized boolean extracted() {
if (ticket == 100) {
return true;
}
else{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket++;
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
}
return false;
}
}
lock锁
手动加锁和释放
public class MyThread extends Thread{
static int ticket = 0;
static Lock lock = new ReentrantLock();//Lock是接口,要新建实现类
@Override
public void run() {
while (true){
lock.lock();;
try{
if(ticket ==100){
break;
}else{
Thread.sleep(10);
ticket++;
System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
}
}
catch (Exception e){
e.printStackTrace();
}
finally {//finally,无论有无异常,这里的代码都会执行
lock.unlock();
}
}
}
}
死锁
线程A占着A锁,线程B拿着B锁,但是两个线程此时想访问对方的锁,就死锁了
生产者和消费者(等待唤醒机制)
线程的执行具有随机性,所以等待唤醒机制可以让执行均匀
比如,桌子上有食物,消费者(线程A)开吃,桌子没有食物,厨师(线程B)开做
执行代码
public static void main(String[] args) throws ExecutionException, InterruptedException {
Cook c= new Cook();
Foodie f = new Foodie();
c.setName("厨师");
f.setName("吃货");
c.start();
f.start();
}
厨师逻辑
public class Cook extends Thread {
public void run() {
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
if (Desk.foodFlag == 1) {
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println("厨师正在做第" + Desk.count + "个汉堡");
Desk.foodFlag = 1;
Desk.lock.notifyAll();
}
}
}
}
}
}
吃货逻辑
public class Foodie extends Thread{
public void run(){
while(true){
synchronized (Desk.lock){
if(Desk.count==0){
break;
}else{
if (Desk.foodFlag==0) {
try {
Desk.lock.wait();//释放当前锁对象,直到有人唤醒
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
Desk.count--;
System.out.println("吃货正在吃第"+Desk.count+"个汉堡");
Desk.lock.notifyAll();//唤醒所有线程
Desk.foodFlag=0;
}
}
}
}
}
}
桌子逻辑
/**
* 控制生产者和消费者的执行
*/
public class Desk {
//0,没有面条,1,有面条
public static int foodFlag = 0;
//总个数
public static int count = 10;
//锁对象
public static Object lock = new Object();
}
阻塞队列实现等待唤醒机制
队列机制,两个线程在一个队列中取和存数据
线程的6种状态
并没有运行状态,为了好理解,可以虚拟出这么一个状态
- 新建
- 就绪
- 阻塞
- 等待wait(),无执行资格无执行权
- 计时sleep(),无执行资格无执行权
- 结束
线程池
-
提交任务时,池子会创建新的线程,执行完毕后,线程归还给池子,再提交任务是,不需要创建新的线程,用已有的线程
-
提交任务没有空闲线程,也无法创建新线程,任务排队等待
public static void main(String[] args) throws ExecutionException, InterruptedException {
//Executors.newCachedThreadPool()//无限线程池
//Executors.newFixedThreadPool()//有上限的线程池
//创建线程池对象
ExecutorService pool1 = Executors.newFixedThreadPool(3);
//提交任务
pool1.submit(new MyRun());
pool1.submit(new MyRun());
pool1.submit(new MyRun());
pool1.submit(new MyRun());
pool1.submit(new MyRun());
}
自定义线程池
-
当核心线程满时,再提交会排队
-
当核心线程满,队伍满时,会创建临时线程
-
当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略
public static void main(String[] args) {
new ThreadPoolExecutor(
3,//核心线程数
6,//最大线程数
60,//存活时间
TimeUnit.SECONDS,//存活时间时间单位
new ArrayBlockingQueue<>(3),//阻塞队列长度
Executors.defaultThreadFactory(),//线程工厂
new ThreadPoolExecutor.DiscardPolicy()//拒绝策略
);
}
最大并行数
我的电脑是32核32线程最大并行,超线程技术可以64线程,是因为实现让1个核分身同时为两个线程服务
线程池合适的大小
CPU密集型运算,最大并行数+1
I/O密集型运算,最大并行数期望CPU利用率((CPU计算时间+等待时间)/CPU计算时间)
多线程的额外扩展
- volatile
- jMM
- 悲观锁\乐观锁\CAS
- 原子性
- 并发工具类