JavaSE基础加强3

多线程

1.创建线程

什么是线程?

    线程就是程序内部当中的一条执行流程;

什么是多线程?线程的代表是谁?线程创建的第一种方式步骤是什么?

    多线程是指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行);

    线程的代表是Thread类;

多线程创建方式一:

    1.定义一个子类继承Thread类,重写run()方法;

    2.创建子类对象代表子类的线程;

    3.调用线程对象的start()方法启动线程;

优点是编码简单;缺点是由于只能单继承的限制,不能继承其他类,不利于功能的扩展;

注意事项:

    1.启动线程必须调用start()方法,而不是run()方法,直接调用run()方法会被当成普通程序执行,此时相当于没有创建一个新的线程;只有调用start()才是启动一个新的线程执行程序;

    2.不要把主线程任务放在启动子线程之前,会默认执行完主线程;    

多线程创建方式二:实现Runnable()接口

    1.定义一个线程任务类实现Runnable()接口,重写run()方法;

    2.创建一个线程任务类对象;

    3.将线程任务类对象交给Thread处理,封装成为线程对象;

    4.调用线程对象的start()方法启动线程;

优点是任务类只是实现接口,可以继续继承其他类,实现其他接口,扩展性强;缺点是需要多一个Runnable对象;

多线程创建方式三:利用Callable()接口,FutureTask类来实现

    1.创建任务对象,定义一个类实现Callable()接口,重写call(),封装要做的事情,和要返回的数据;把Callable类型的对象封装成FutureTask(线程任务对象);

    2.把线程任务对象交给Thread对象;

    3.调用Thread对象的start()方法启动线程;

    4.线程执行完毕之后,通过FutureTask对象的get()方法去获取线程任务执行的结果;

优点是线程任务类只是实现接口,可以继续继承其他类,实现其他接口,扩展性强,还可以在线程执行完毕之后获取线程任务执行的结果;缺点是编码比较复杂;

2.线程安全

什么是线程安全问题?出现线程安全的原因?

    多个线程,同时操作同一个共享资源的时候,可能出现业务安全问题;

    存在多个线程同时执行,同时访问一个共享资源,存在对共享资源进行修改的操作;

2.1 线程同步

    作用:线程同步是线程安全的解决方案;

    思想:让多个线程先后依次访问共享资源,这样就可以避免出现线程安全问题;

    常见方案:加锁;

什么是加锁? 每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能加锁进来;

2.2 加锁的常见方式

Test代码:

package synchronizedDemo1;

public class Test {
    public static void main(String[] args) {
        //目标:模拟线程安全问题
        //小红小明同时去同一个账号取钱
        //创建一个账户类,用来存放100000元
        Account acc = new Account("GYID-123456",100000);
        //创建小明,小红两个线程,并启动线程

        Thread xm = new DrawThread("小明",acc);
        xm.start();
        Thread xh = new DrawThread("小红",acc);
        xh.start();
    }
}

账户Account代码:

package Thread;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor

public class Account {
    private String AccCard;
    private double AccMoney;

    public void DrawMoney(double Money) {
        String name = Thread.currentThread().getName() ;
        if (AccMoney >= Money) {
            System.out.println(name + "取钱"+ Money +"成功" );
            AccMoney -= Money;
            System.out.println(name + "余额为:" + AccMoney);
        } else {
            System.out.println(name  + "取钱失败,余额不足");
        }
    }
}
package Thread;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor

public class Account {
    private String AccCard;
    private double AccMoney;

    public void DrawMoney(double Money) {
        String name = Thread.currentThread().getName() ;
        if (AccMoney >= Money) {
            System.out.println(name + "取钱"+ Money +"成功" );
            AccMoney -= Money;
            System.out.println(name + "余额为:" + AccMoney);
        } else {
            System.out.println(name  + "取钱失败,余额不足");
        }
    }
}
1.同步代码块

    作用:把访问共享资源的核心代码块上锁,以保证线程安全;

    原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行;

    同步锁的注意事项:对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug;

    锁对象的使用规范建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象;对于静态方法,建议使用字节码(类名.class)对象作为锁对象;

package synchronizedDemo1;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor

public class Account {
    private String AccCard;
    private double AccMoney;

    public void DrawMoney(double Money) {
        String name = Thread.currentThread().getName() ;
        synchronized (this) {
            if (AccMoney >= Money) {
                System.out.println(name + "取钱"+ Money +"成功" );
                AccMoney -= Money;
                System.out.println(name + "余额为:" + AccMoney);
            } else {
                System.out.println(name  + "取钱失败,余额不足");
            }
        }
    }
}
2.同步方法

    作用:把访问共享资源的核心方法给上锁,以保证线程安全;

    原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行;

    底层原理:同步方法底层也是有隐式锁对象的,只是锁的范围是整个代码,实例方法默认使用this作为锁对象,静态方法默认使用字节码(类名.class)对象作为锁对象;

测试用例:

package synchronizedDemo2;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor

public class Account {
    private String AccCard;
    private double AccMoney;

    public synchronized void DrawMoney(double Money) {
        String name = Thread.currentThread().getName() ;
            if (AccMoney >= Money) {
                System.out.println(name + "取钱"+ Money +"成功" );
                AccMoney -= Money;
                System.out.println(name + "余额为:" + AccMoney);
            } else {
                System.out.println(name  + "取钱失败,余额不足");
            }
    }
}

3.Lock锁

    Lock锁是JDK5开始提供的一种新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活,更强大,更方便;Lock是接口,不能直接实例化,可以采用他的实现类ReentrantLock来构建Lock锁对象;

测试用例:

package synchronizedDemo3;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Data
@NoArgsConstructor
@AllArgsConstructor

public class Account {
    private String AccCard;
    private double AccMoney;
    private final Lock lk= new ReentrantLock();

    public void DrawMoney(double Money) {
        String name = Thread.currentThread().getName() ;
        lk.lock();
        try {
            if (AccMoney >= Money) {
                System.out.println(name + "取钱"+ Money +"成功" );
                AccMoney -= Money;
                System.out.println(name + "余额为:" + AccMoney);
            } else {
                System.out.println(name  + "取钱失败,余额不足");
            }
        } finally {
            lk.unlock();
        }
    }
}

    建议将锁对象加上final修饰,避免呗篡改;建议将释放锁的操作放到finally代码块中,确保锁用完了一定会被释放; 

3.线程池

    线程池就是一个可以复用线程的技术;

3.1 创建线程池

    JDK5.0起提供了代表线程池的接口:ExecutorService;

    方式一:使用ExecutorService的实现类ThreadPoolExecutor自己创建一个线程池对象;

    方式二:使用Executors(线程池的工具类)调用方法返回不同的线程池对象;

3.2 线程池的注意事项

    什么时候开始创建临时线程?

    新任务提交时发现核心任务都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程;

    什么时候会拒绝新任务?

    核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会拒绝任务;

3.3 通过Executors工具类创建线程池

    是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象;

3.4 并发/并行

    进程:正在运行的程序或者软件就是一个进程;线程是属于进程的,一个进程中可以同时运行多个线程;进程中的多个线程其实是并发或者并行执行的;

    并发:进程中的线程是由CPU负责调度执行的,但CPU能同时处理的线程数量有限,为了保证全部线程都能往前执行,CPU会轮询位系统的每个线程服务,由于CPU切换的速度很快,给我们感觉这些线程都在同时执行,这就是并发;

    并行:在同一个时刻上,同时有多个线程在被CPU调度执行;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值