1.进程的概念
进程是指可执行程序并存放在计算机存储器的一个指令序列,它是一个动态执行的过程。
简单来说电脑上运行的QQ、微信、谷歌浏览器分别就是一个一个的进程,它们之所以看着像同时运行,其实不然,在CPU中,每个软件是轮流运行的,而且每个运行的时间间隔很短,作为我们使用者来说,是感觉不到它的变化的。所以我们就会认为软件都是同时运行的,叫做时间片的轮转。
2.什么是线程
线程是比进程还要小的运行单位,一个进程包含多个线程(比如说一个程序是有很多行代码组成的,那么这些代码就可以分为很多块,放到不同的线程去分别执行),线程可以看做一个子程序
3.线程的创建
- 创建一个Thread类,或者Thread子类的对象
- 创建一个实现Runnable接口的类的对象
Thread类的构造方法
Thread类的常用方法
Runnable接口
4.通过Thread类创建线程
package com.imooc.thread;
public class MyThread extends Thread{
public MyThread(String name){
super(name);//调用父类的有参构造犯法
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(getName()+"线程正在执行"+i);
}
}
}
package com.imooc.thread;
public class ThreadTest {
public static void main(String[] args) {
MyThread mt1 = new MyThread("线程1");
MyThread mt2 = new MyThread("线程2");
mt1.start();//启动线程1
mt2.start();//启动线程2
}
}
5.通过Runnable接口创建线程
package com.imooc.runnable;
public class PrintRunnable implements Runnable {
public void run() {
System.out.println(Thread.currentThread().getName()+"正在运行");
}
}
package com.imooc.runnable;
public class Test {
public static void main(String[] args) {
//通过实现Runnable接口来创建线程
PrintRunnable printRunnable = new PrintRunnable();
Thread thread = new Thread(printRunnable);
thread.start();
}
}
6.线程的状态和生命周期
线程的状态
新建(New)
可运行(Runnable)
正在运行(Running)
阻塞(Blocked)
中止(Dead)
线程的生命周期
7.sleep方法的使用
Thread类的静态方法
作用:在指定的毫秒数让正在执行的线程休眠(暂停执行)
参数为休眠的时间,单位是毫秒
package com.imooc.runnable;
public class PrintRunnable implements Runnable {
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+"正在运行第"+i+"次");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
8.join方法的使用
Thread类的方法
public final void join() 最终的方法,不能被重写
作用:等待调用join方法的线程结束后才能执行(是一种抢占资源的方式)
package com.imooc.runnable;
public class Test {
public static void main(String[] args) {
//通过实现Runnable接口来创建线程
PrintRunnable printRunnable = new PrintRunnable();
Thread thread = new Thread(printRunnable);
thread.start();
try {
//调用了join方法的线程优先执行
//如果加了join方法加了参数毫秒,那么意味着该线程最多执行多少毫秒后,就会把程序执行主动权放出来
thread.join();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("主线程运行结束");
}
}
9.线程的优先级
- Java为线程类提供了10个优先级
- 优先级可以用整数1-10表示,超过范围会抛出异常
- main方法主线程默认优先级为5
- 数字越大表示优先级越高
还可以用优先级常量表示
MAX_PRIORITY:线程的最高优先级10
MIN_PRIORITY:线程的最低优先级1
NORM_PRIORITY:线程的默认优先级5
优先级相关的方法
特别说明:优先级大的不一定先执行,这跟系统的操作系统的环境,CPU的工作方式都是有很大的关系
package com.imooc.runnable;
public class PrintRunnable implements Runnable {
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "正在运行第" + i + "次");
}
}
}
package com.imooc.runnable;
public class Test2 {
public static void main(String[] args) {
PrintRunnable pr = new PrintRunnable();
PrintRunnable pr2 = new PrintRunnable();
Thread t1 = new Thread(pr,"我是线程1");//线程2
Thread t2 = new Thread(pr2,"我是线程2");//线程2
t1.setPriority(Thread.MIN_PRIORITY);//设置最小优先级
t2.setPriority(Thread.MAX_PRIORITY);//设置最大优先级
//说明:优先级大的不一定先执行,这跟系统的操作系统的环境,CPU的工作方式都是有很大的关系
//启动线程
t1.start();
t2.start();
}
}
执行效果:
10.线程同步
多线程的运行问题
- 各个线程是通过竞争CPU时间而获得运行机会的
- 各线程什么时候得到CPU时间,占用多久,是不可预测的
- 一个正在运行的线程什么地方被暂停是不确定的
银行存取款问题
- 为了包装存款或取款的时候,不允许其他线程对账号余额进行操作
- 需要将Bank对象进行锁定
- 使用关键字synchronized实现
具体代码
package com.imooc.runnable;
public class Bank {
private String account;//账号
private int balance;//账户余额
public Bank(String account, int balance) {
this.account = account;
this.balance = balance;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Bank{" +
"account='" + account + '\'' +
", balance=" + balance +
'}';
}
//存款方法 synchronized:同步操作,当前方法没执行完的时候不允许其他线程操作
public synchronized void saveAccount(){
//获取余额
int balance = getBalance();
//存100元进去
balance+=100;
//模拟正常的耗时操作
try {
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
//保存余额
setBalance(balance);
System.out.println("存款后的账户余额为:"+balance);
}
//取款方法 synchronized:同步操作,当前方法没执行完的时候不允许其他线程操作
public void drawAccount(){
synchronized (this){
int balance = getBalance();
//取200元
balance -= 200;
//模拟正常的耗时操作
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
//保存余额
setBalance(balance);
System.out.println("取款后的账户余额为:" + balance);
}
}
}
package com.imooc.runnable;
public class SaveAccount implements Runnable{
//声明一个bank对象
Bank bank;
public SaveAccount(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
//使用线程进去存款操作
bank.saveAccount();
}
}
package com.imooc.runnable;
public class DrawAccount implements Runnable{
Bank bank;
public DrawAccount(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
//使用线程进行取款操作
bank.drawAccount();
}
}
package com.imooc.runnable;
public class BankTest {
public static void main(String[] args) {
//声明银行对象,设置账号和初始金额
Bank bank = new Bank("1001",1000);
//创建线程
//1.存款线程
SaveAccount saveAccount = new SaveAccount(bank);
//2.取款线程
DrawAccount drawAccount = new DrawAccount(bank);
Thread thread1 = new Thread(saveAccount);
Thread thread2 = new Thread(drawAccount);
//3.启动线程
thread1.start();
thread2.start();
//4.优先执行两个线程
try{
thread1.join();
thread2.join();
}catch (Exception e){
e.printStackTrace();
}
System.out.println(bank);
}
}
上述问题就和并发问题类似,多线程抢占资源,就比如商品超卖问题(比如在商品数量还剩余1的情况下,A线程正在进行判断商品的数量是否合法,接下来执行扣完库存操作,然而B线程却在A线程还没来得及执行扣库存操作的情况下也进入了这个商品判断,导致B线程也执行了扣库存操作,这就导致了商品为负数)在PHP项目中我之前采用的Redis分布式锁+lua脚本执行原子操作解决的这个问题。
11.线程间通信
- wait()方法:中断方法的执行,使线程等待
- notify()方法:唤醒处于等待的某一线程,使其结束等待
- notifyAll()方法:唤醒所有处于等待的线程,使它们结束等待
队列类:
package com.imooc.queue;
public class Queue {
private int n;
boolean flag = false;
public synchronized int getN() {
//如果没数据则等待
if (!flag) {
try {
wait();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("消费" + n);
flag = false;
//唤醒所有处于等待的线程
notifyAll();
return n;
}
public synchronized void setN(int n) {
//如果有数据,则等待
if(flag) {
try {
wait();
}catch (Exception e){
e.printStackTrace();
}
}
System.out.println("生产" + n);
this.n = n;
flag = true;
//唤醒所有处于等待的线程
notifyAll();
}
}
生产类:
package com.imooc.queue;
/**
* 生产类
*/
public class Pro implements Runnable {
Queue queue;
public Pro(Queue queue) {
this.queue = queue;
}
@Override
public void run() {
int i=0;
while (true) {
queue.setN(i++);
//休眠1s
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
消费类:
package com.imooc.queue;
public class Cons implements Runnable {
Queue queue;
public Cons(Queue queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
queue.getN();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
测试类:
package com.imooc.queue;
public class Test {
public static void main(String[] args) {
Queue queue = new Queue();
//生产者
Pro pro = new Pro(queue);
//消费者
Cons con = new Cons(queue);
Thread thread1 = new Thread(pro);
Thread thread2 = new Thread(con);
thread1.start();
thread2.start();
}
}
实现效果: