线程

相关概念

  • 程序Program
    是为实现特定目标或解决特定问题而用计算机语言编写的命令序列的集合
  • 进程Process
    • 是程序关于某个数据集合上的一次运行活动(对应一个exe),是独立运行的程序(正在运行的程序);对应了从代码加载、执行至执行完毕的一个完整过程;
    • 目前操作系统都是支持多进程,可以同时执行多个进行,通过进程PID区分
    • 是系统进行资源分配和调度的一个独立单位

  • 线程Thread
    • 是进程的一个实体,CPU调度和分派的基本单位,是比进程更小的、能独立运行的基本单位
    • 一个应用程序中有多条并发执行的线索,每条线索都被称为一个线程。各线程交替执行,彼此间可以通信

  • 线程与进程的关系
    • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程
    • 操作系统把资源分配给进程,而同一进程的所有线程共享该进程的所有资源
    • 进程是拥有资源的基本单位,而线程是作为CPU调度和分配的基本单位

创建线程

  • 创建线程的三种方式
    • 继承Thread类,重写run方法
    • 实现Runnable接口
    • 实现Callable接口

继承Thread

1
2
3
4
5
6
7
8
9
//创建线程类
public class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("子线程....."+i);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class Demo01{
public static void main(String[] args) {
//1. 创建线程对象
MyThread myThread = new MyThread();
//2. 启动线程 不能直接调用run方法
myThread.start(); //会去调用run方法
//主线程执行
for(int i=0;i<50;i++){
System.out.println("主线程===="+i);
}
}
}

由于线程运行是抢占式的,两个线程执行结果顺序是不同的

获取线程名称

  • 第一种方法:在Thread的子类中调用this.getId()this.getName()
1
2
3
4
5
6
7
8
9
10
11
public class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(
"线程id:"+this.getId()+
"线程名称"+this.getName()+
"子线程....."+i);
}
}
}
  • 第二种方法【推荐】:使用Thread.currentThread().getId()Thread.currentThread().getName()
1
2
3
4
5
6
7
8
9
10
11
12
//第二种方法 Thread.currentThread()取得当前线程
public class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(
"线程id:"+Thread.currentThread().getId()+
"线程名称"+Thread.currentThread().getName()+
"子线程....."+i);
}
}
}

修改线程名称

  • 第一种方法:调用线程对象的setName()方法
1
2
3
4
MyThread myThread1 = new MyThread();
//修改线程名称
myThread1.setName("子线程1");
myThread1.start();
  • 第二种方法:使用线程子类的构造方法赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//首先给线程类添加有参构造方法
public class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name); //传给Thread
}
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(
"线程id:"+Thread.currentThread().getId()+
"线程名称"+Thread.currentThread().getName()+
"子线程....."+i);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Demo01{
public static void main(String[] args) {
//1. 创建线程对象
MyThread myThread1 = new MyThread("子线程1");
myThread1.start();
MyThread myThread2 = new MyThread("子线程2");
myThread2.start();
//主线程执行
for(int i=0;i<50;i++){
System.out.println("主线程===="+i);
}
}
}

卖票案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class TicketWin extends Thread{
private int ticket = 100; //票
public TicketWin(){

}
public TicketWin(String name){
super(name);
}
@Override
public void run() {
//买票功能
while(true){
if(ticket<=0){
break; //没有票了
}
System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
ticket--;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Demo01{
public static void main(String[] args) {
//创建四个线程对象
TicketWin w1 = new TicketWin("窗口1");
TicketWin w2 = new TicketWin("窗口2");
TicketWin w3 = new TicketWin("窗口3");
TicketWin w4 = new TicketWin("窗口4");
//启动线程
w1.start();
w2.start();
w3.start();
w4.start();
}
}

实现Runnable接口

1
2
3
4
5
6
7
8
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"====="+i);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Demo01{
public static void main(String[] args) {
//1.创建Runnable对象 表示线程要执行的功能
MyRunnable myRunnable = new MyRunnable();
//2. 创建线程对象 交给Thread
Thread thread = new Thread(myRunnable,"线程1");
//3. 启动
thread.start();
for(int i=0;i<50;i++){
System.out.println("主线程......."+i);
}
}
}

使用匿名内部类来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Demo01{
public static void main(String[] args) {
//使用匿名内部类 创建可执行对象
Runnable runnable = new Runnable() {
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"====="+i);
}
}
};
//创建线程对象来运行
Thread thread = new Thread(runnable,"线程1");
//启动线程
thread.start();
}
}

线程状态

基本状态

线程休眠

  • public static void sleep(long millis) 当前线程主动休眠,单位为毫秒数。静态方法可以通过类名调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name); //传给Thread
}
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(
"线程id:"+Thread.currentThread().getId()+
"线程名称"+Thread.currentThread().getName()+
"子线程....."+i);
try { //不能抛出
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
1
2
3
4
5
6
7
8
public class Demo01{
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
MyThread myThread1 = new MyThread();
myThread1.start();
}
}

运行结果是每隔1秒打印一次

线程放弃

  • public static void yield() 当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name); //传给Thread
}
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(
"线程id:"+Thread.currentThread().getId()+
"线程名称"+Thread.currentThread().getName()+
"子线程....."+i);
//主动放弃CPU
Thread.yield();
}
}
}
1
2
3
4
5
6
7
8
public class Demo01{
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread myThread1 = new MyThread();
myThread.start();
myThread1.start();
}
}

执行结果发现有很多次交替输出的情况

线程加入

  • public final void join() 允许其他线程加入到当前线程中,不是静态方法,需要创建实例调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name); //传给Thread
}
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(
"线程id:"+Thread.currentThread().getId()+
"线程名称"+Thread.currentThread().getName()+
"子线程....."+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Demo01{
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
//主线程
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+"====="+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

未使用join方法时,两个线程是差不多交替执行的,并没有出现阻塞的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Demo01{
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
try {
myThread.join(); //加入当前线程(mian主线程)并阻塞当前线程,直到加入线程执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
//主线程
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+"====="+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

使用join方法后,主线程被阻塞了,必须使子线程运行完后才能运行主线程

线程优先级

  • 设置线程优先级
    • 使用线程对象调用setPriority() 方法即可
    • 线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name); //传给Thread
}
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(
"线程id:"+Thread.currentThread().getId()+
"线程名称"+Thread.currentThread().getName()+
"子线程....."+i);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class Demo01{
public static void main(String[] args) {
MyThread myThread = new MyThread("线程1");
MyThread myThread1 = new MyThread("线程2");
MyThread myThread2 = new MyThread("线程3");
myThread.setPriority(1);
myThread2.setPriority(10);
myThread.start();
myThread1.start();
myThread2.start();
}
}

运行结果发现,线程3的执行顺序要靠前,也就是其优先级高,优先抢占CPU

守护线程

  • 设置守护线程
    • 使用线程对象调用setDaemon(true)设置为守护线程
  • 线程有两类:用户线程(前台线程)、守护线程(后台线程)
  • 如果程序中所有前台线程都执行完毕了,后台线程会自动结束。垃圾回收器线程属于守护线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name); //传给Thread
}
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(
"线程id:"+Thread.currentThread().getId()+
"线程名称"+Thread.currentThread().getName()+
"子线程....."+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Demo01{
public static void main(String[] args) {
//创建线程(默认前台线程)
MyThread myThread = new MyThread("线程1");
//设置为守护线程(后台线程)
myThread.setDaemon(true);
//前台线程(mian主线程)执行完毕,守护线程会自动结束
myThread.start();
for(int i=0;i<10;i++){
System.out.println("主线程:-----"+i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

未设置为守护线程时,线程直到循环完才结束,设置为守护线程后,主线程运行完毕,守护线程就不再执行了

线程等待

线程同步

同步代码块

  • 多线程安全问题
    • 当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致
    • 临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性
    • 原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省
  • 同步代码块
1
2
3
synchronized(临界资源){ //对临界资源对象加锁
//代码(原子操作)
}
  • 每个对象都有一个互斥锁标记,用来分配给线程的
  • 只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块
  • 线程退出同步代码块时,会释放相应的互斥锁标记
1
2
3
4
5
6
7
8
9
10
11
public class BankCard {
private double money;

public double getMoney() {
return money;
}

public void setMoney(double money) {
this.money = money;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class testDemo {
public static void main(String[] args) {
//1. 创建银行卡
final BankCard card = new BankCard();
//2. 创建两个操作
Runnable add = new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++){
synchronized (this) {
card.setMoney(card.getMoney() + 1000);
System.out.println(Thread.currentThread().getName() + "存了1000,余额是:" + card.getMoney());
}
}
}
};
Runnable sub = new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++){
synchronized (this) {
if (card.getMoney() >= 1000) {
card.setMoney(card.getMoney() - 1000);
System.out.println(Thread.currentThread().getName() + "取了1000,余额是:" + card.getMoney());
} else {
System.out.println("余额不足,请存钱");
i--;
}
}
}
}
};
//3. 创建两个线程对象
Thread xiaoli = new Thread(add,"小李");
Thread xiaoming = new Thread(sub,"小明");
xiaoli.start();
xiaoming.start();
}
}

同步方法

1
2
3
synchronized 返回值类型 方法名称(形参列表){ //对当前对象(this) 加锁
//代码(原子操作)
}
  • 只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步方法中
  • 线程退出同步方法时,会释放相应的互斥锁标记
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class TicketWin extends Thread{
private int ticket = 100; //票
//创建锁
private Object obj = new Object();
public TicketWin(){

}
public TicketWin(String name){
super(name);
}
@Override
public void run() {
//买票功能
while(true){
if(!sale()){
break;
}
}
}
//同步方法
public synchronized boolean sale(){ //锁是this 如果为静态方法就是当前的类
if(ticket<=0){
return false; //没有票了
}
//如果不加同步机制 是会出现重复的数据,因为还没执行ticket--时间片就被抢占了
System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
ticket--;
return true;
}
}

线程通信

  • 等待:
    • public final void wait()
    • public final void wait (long timeout)
    • 必须在对obj加锁的同步代码块中。在一个线程中,调用obj.wait() 时,此线程会释放其拥有的所有锁标记。同时此线程阻塞在obj的等待队列中。释放锁,进入等待队列。
  • 通知:
    • public final void notify()
    • public final void notifyAll()

注意:一定是锁.wait() 或者锁.notify()

存钱取钱

1
2
3
4
5
6
7
8
9
10
11
12
public class AddMoney implements Runnable{
private BankCard card;
public AddMoney(BankCard card){
this.card=card;
}
@Override
public void run() {
for(int i=0;i<10;i++){
card.save(1000);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class SubMoney implements Runnable{
private BankCard card;
public SubMoney(BankCard card){
this.card=card;
}
@Override
public void run() {
for(int i=0;i<10;i++){
card.take(1000);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class BankCard {
//余额
private double money;
//标记
private boolean flag; //true表示有钱可以取钱 false表示没钱可以存取
public double getMoney() {
return money;
}

public void setMoney(double money) {
this.money = money;
}
//存取
public synchronized void save(double m){ //锁为this
while(flag){ //有钱 不能存取了 相对于进入存钱功能的一道坎
try {
this.wait(); //加入等待队列,同时释放锁和CPU
} catch (InterruptedException e) {
e.printStackTrace();
}
}
money=money+m;
System.out.println(Thread.currentThread().getName()+"存了"+m+"余额是"+getMoney());
//修改标记
flag=true; //有钱了
//唤醒取钱线程
this.notify();
}
//取钱
public synchronized void take(double m){
while(!flag){ //没钱 相对于进入取钱功能的一道坎
try {
this.wait(); //加入等待队列,同时释放锁和CPU
} catch (InterruptedException e) {
e.printStackTrace();
}
}
money=money-m;
System.out.println(Thread.currentThread().getName()+"取了"+m+"余额是"+getMoney());
//修改标记
flag=false; //取完没钱了
//唤醒存钱线程
this.notify();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class testDemo {
public static void main(String[] args) {
//1. 创建银行卡
BankCard card = new BankCard();
//2. 创建操作
AddMoney add = new AddMoney(card);
SubMoney sub = new SubMoney(card);
//3. 创建线程对象
Runnable target;
Thread xiaoli = new Thread(add,"小李");
Thread xiaowang = new Thread(sub,"小王");
//4. 启动
xiaoli.start();
xiaowang.start();
}
}

执行结果是存一次然后取一次,线程之间有明显的执行顺序

生产者消费者问题

  • 若干个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个能存储多个产品的缓冲区,生产者将生产的产品放入缓冲区中,消费者从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个满的缓冲区中放入产品
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//面包
public class Bread {
private int id;
private String productName;

public Bread() {
}

public Bread(int id, String productName) {
this.id = id;
this.productName = productName;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getProductName() {
return productName;
}

public void setProductName(String productName) {
this.productName = productName;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//面包容器
public class BreadCon {
//存放面包的数组
private Bread[] cons = new Bread[6];
//存放面包的位置
private int index = 0;
//放入面包
public synchronized void input(Bread bread){ //锁 this
//判断容器有没有满
while(index>=6){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
cons[index]=bread;
System.out.println(Thread.currentThread().getName()+"生产了"+ bread.getId());
index++;
//唤醒消费者
this.notifyAll();
}
//取出面包
public synchronized void output(){ //锁 this
//判断有没有面包
while(index<=0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
index--;
Bread bread = cons[index];
System.out.println(Thread.currentThread().getName()+"消费了"+ bread.getId()+"生产者:"+bread.getProductName());
cons[index] = null;
//唤醒生产者
this.notifyAll();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//生产者
public class Product implements Runnable{
private BreadCon con;

public Product(BreadCon con) {
this.con = con;
}

@Override
public void run() {
for(int i=0;i<30;i++){
con.input((new Bread(i,Thread.currentThread().getName())));
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//消费者
public class Consume implements Runnable {
private BreadCon con;

public Consume(BreadCon con) {
this.con = con;
}

@Override
public void run() {
for(int i=0;i<30;i++){
con.output();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class testDemo {
public static void main(String[] args) {
//容器
BreadCon con = new BreadCon();
//生产和消费
Product product = new Product(con);
Consume consume = new Consume(con);
//创建线程duix1
Runnable target;
Thread chenchen = new Thread(product,"晨晨");
Thread bingbing = new Thread(consume,"冰冰");
//启动线程
chenchen.start();
bingbing.start();
}
}