文章目录
多线程
什么是程序?
程序:程序员编写的功能代码就是程序。
进程:正在运行中的程序就是进程。
线程:进程的执行单元/场景。
线程的常用方法
- Thread.sleep(long time):让线程进入指定的休眠时间
- yield():让线程放弃当前CUP时间片,回到可运行状态
- join():让当前线程暂停执行进入阻塞状态,等待其它线程执行完毕后再执行
- interrupt():终止线程的睡眠
- stop():强行终止线程
- wait():让当前对象线程进入等待
- notify():通知当前任意一个线程对象可以释放锁(即让wait()方法退出等待)
- notifyAll():通知当前线程对象的所有等待线程释放锁
- start():启动线程
创建线程对象
创建线程对象的方法有三个:
- 继承Thread类,并重写run方法
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("分支线程---->"+i);
}
}
}
public class ThreadTest02 {
public static void main(String[] args) {
//创建分支线程对象
MyThread myThread = new MyThread();
//启动线程 调用start方法
myThread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("主线程---->"+i);
}
}
}
- 实现Runnable接口,重写run方法
public class ThreadTest03 {
public static void main(String[] args) {
//创建可运行对象
MyRunnable myRunnable = new MyRunnable();
//将可运行对象封装程一个线程对象
Thread myThread = new Thread(myRunnable);
//合并以上代码
Thread myThread = new Thread(new MyRunnable());
//启动线程
myThread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("主线程--->"+i);
}
}
}
//这不是一个线程类,是一个可运行的类。它还不是一个线程。
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("分支线程---->"+i);
}
}
}
- 实现Callable类,再创建FutureTask对象在构造方法中传入Callable实现类对象,然后创建Thread对象在构造方法中传入FutureTask对象
/*
实现线程的第三种方式:
实现Callable接口
*/
public class ThreadTest15 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//第一步:产生Callable实现类对象
Callable c = new MyCallable();
//第二步:创建一个“未来任务“类对象,传入Callable实现类对象
FutureTask task = new FutureTask(c);
//创建线程对象
Thread t = new Thread(task);
t.start();
//这里是main方法,这是在主线程中
//主线程怎么获取t线程的返回结果?
//get()方法的执行会导致当前线程阻塞。
Object obj = task.get();
System.out.println("线程执行结果:"+obj);
//main方法这里的程序要想执行必须等待get()方法的结束
//而egt()方法可能需要很久。因为get()方法是为了拿另一个线程的执行结果
//另一个线程执行是需要时间的
}
}
class MyCallable implements Callable{
@Override
public Object call() throws Exception {// call()方法就相当于run方法只不过这个有返回值。
//线程执行一个任务,执行之后可能会有一个执行结果
//模拟执行
System.out.println("call method begin");
Thread.sleep(1000*10);
System.out.println("call method end");
int a =100;
int b =100;
return a+b;
}
}
线程的调度模式
线程的调度采用的是抢占式调度模式。什么意思呢?就是说线程之间需要抢夺cup时间片的使用权。
cup时间片:cup分配给线程的使用时间。
线程也是有优先级的。在java中主线程(main方法)和子线程(自定义线程)的默认优先级都是一样的,都是5。而且并不是优先级高的就一定先抢到cup时间片。而是抢到的几率比较高而已。
线程的状态
线程的状态:
- 新建状态(准备/初始状态)(new线程对象)
- 可运行状态(start方法)
- 运行状态(被CUP选中)
- 终止状态(stop方法)
- 阻塞状态(sleep/join方法)
- 等待状态(wait方法)
线程的分类
线程的分类:
- 用户线程:
<1>主线程
<2>子线程 - 守护线程:
作用:在后台默默的为用户线程提供服务。例如:垃圾回收线程
设置线程为守护线程方法:setDaemon(true)
守护线程的生命周期:当所有的用户线程结束,守护线程也结束
定时器
定时器Timer:间隔特定的时间,执行特定的程序。
//假设这是一个记录日志的定时任务
class LogTimerTask extends TimerTask {
public void run(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime = sdf.format(new Date());
System.out.println(strTime+"成功完成了一次数据备份");
}
}
/*
使用定时器指定定时任务
*/
public class TimerTest {
public static void main(String[] args) throws ParseException {
//创建定时器对象
Timer timer = new Timer();
//Timer timer = new Timer(true); 守护线程的方式
//指定定时任务
//timer.schedule(定时任务,第一次执行时间,间隔多久执行一次);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
Date firstTime = sdf.parse("2021-03-27 21:35:00 000");
//timer.schedule(new LogTimerTask(),firstTime,1000*10);
//使用匿名内部类的方式
timer.schedule(new TimerTask() {
@Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime = sdf.format(new Date());
System.out.println(strTime+"成功完成了一次数据备份");
}
}, firstTime, 1000 * 10);
}
}
线程同步
线程同步:线程同步是为了解决多线程执行中数据不一致的问题。
例如:
/*
模拟线程并发下的数据安全问题
*/
public class ThreadTest {
public static void main(String[] args) {
// 创建账户对象(只创建1个)
Account act = new Account("act-001", 10000);
// 创建两个线程
Thread t1 = new AccountThread(act);
Thread t2 = new AccountThread(act);
// 设置name
t1.setName("t1");
t2.setName("t2");
// 启动线程取款
t1.start();
t2.start();
}
}
/*
银行账户
不使用线程同步机制,多线程对同一个账户进行取款,出现线程安全问题。
*/
class Account {
// 账号
private String actno;
// 余额
private double balance;
public Account() {
}
public Account(String actno, double balance) {
this.actno = actno;
this.balance = balance;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
//取款的方法
public void withdraw(double money){
// t1和t2并发这个方法。。。。(t1和t2是两个栈。两个栈操作堆中同一个对象。)
// 取款之前的余额
double before = this.getBalance(); // 10000
// 取款之后的余额
double after = before - money;
// 在这里模拟一下网络延迟,100%会出现问题
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 更新余额
// 思考:t1执行到这里了,但还没有来得及执行这行代码,t2线程进来withdraw方法了。此时一定出问题。
this.setBalance(after);
}
}
//取款线程
class AccountThread extends Thread{
// 两个线程必须共享同一个账户对象。
private Account act;
// 通过构造方法传递过来账户对象
public AccountThread(Account act) {
this.act = act;
}
public void run(){
// run方法的执行表示取款操作。
// 假设取款5000
double money = 5000;
// 取款
// 多线程并发执行这个方法。
act.withdraw(money);
System.out.println(Thread.currentThread().getName() +
"对"+act.getActno()+"取款"+money+"成功,余额" + act.getBalance());
}
}
运行结果:
t1对act-001取款5000.0成功,余额5000.0
t2对act-001取款5000.0成功,余额5000.0
可以看到,我们的账户明明只有10000元,但是取款了10000元却还剩下5000元。这明显是不合理的。那么此时我们就可以使用线程同步机制来使得我们的数据变得合理。
线程同步其实就是一种锁,这种锁叫排他锁。例如:线程t1和线程t2,在线程t1执行的时候必须要等线程t2执行结束,或者线程t2执行的时候必须要等线程t1执行结束。两个线程之间有等待关系。其实就是:线程排队执行。
那么我们怎么实现线程同步呢?
线程同步的关键字是:synchronized
使用方法:
- 在静态 方法声明的时候加上synchronized关键字。这种方法得到的锁称为类锁。
public static synchronized 返回值 方法名(形式参数列表){
代码...
}
类锁:顾名思义。就是锁住了整一个类。当我们调用的是类锁的时候,那么这个类就只能被一个线程访问,即使这个类中有其它非静态方法也不允许被其它线程访问。就好比一栋房子,房子里很多房间,但是,当一个人进去入房子后其他人不允许再进去了,虽然里面还有很多房间,但是其它人此时无法进入,就是这么霸道。
对象锁:对象锁,正好与类锁相反。当一个人进去之后,其他人也能进去,但是这个人进去后找到一个房间并进去了,此时这个房间是不允许其他人进入的,与类锁相比就没那么霸道。对象所只占据房子的一个房间,类似与类中的一个实例方法,二类锁霸占的是整一个类。
类锁只有一把,就算创建了100个对象,那类锁也只有一把。
- 在实例方法上使用synchronized表示共享对象一定是this并且同步代码块是整个方法,这是一个对象锁。
但是这种方法有一个缺点:synchronized出现在实例方法上,表示整个方法体都需要同步,可能会无故扩大同步的范围,**导致程序的执行效率降低。**所以这种方式不常用。
public synchronized void 方法名(形式参数列表){
代码...
}
- 在方法中使用synchronized(要锁的对象){代码…}。这是一个对象锁。
public void 方法名(形式参数列表){
synchronized(对象,可以是this也可以是Object,写你要锁的对象即可){
代码...
}
}
高级线程同步(可以了解也可以不了解)
高级线程同步中synchronized被替换成了Lock。
这里建议各位查文档研究一番。当然不研究也行,我只是想工资高亿点点所以才研究了一下下。
这里就不多做赘述了。多线程基础到此为止。有错请指出。看过给个♥,让我知道我不是一个人~