多线程

内容

  • 什么是线程
  • 如何创建线程
  • 线程的调度
  • 线程的一个设计模式:生产消费者模型
  • 线程池
  • 线程集合对象(侧重点)

一、什么是线程

进程:运行中的程序才可以称为进程,一个程序一个进程。宏观并行,微观串行。

线程:

1.任何一个程序都至少拥有一个线程,即主线程。但是java程序默认有两个线程,除了主线程之外,还有一个线程,即用于垃圾回收的守护线程。

2.线程是一种轻量级进程,在CPU当中的最基本单元是线程。

3.一个进程可以包含若干的线程

4.各进程之间不共享内存,同进程的各线程之间共享内存

5.每个线程都有独立的栈空间

6.堆当中的地址,可以被共享

注意:

线程是不可控的,只能调度,无法精确控制。研究线程,研究的是如何在多线程场景中,保证数据读写的安全,以及调度线程的运行状态

二、线程的创建和使用

1.通过Thread类来进行创建,需要继承Thread类

package com.mine.demo01;
public class Main1 {
    public static void main(String[] args) {
        MyThread1 mt1 = new MyThread1();
        mt1.start();//调用的不是run方法,而是start
    }
}
class MyThread1 extends Thread{
    @Override
    public void run() {
        System.out.println("线程运行了");
    }
}

2.通过实现Runnable接口来创建

package com.mine.demo01;
public class Main2 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyThread2());
        t.start();
    }
}
class MyThread2 implements Runnable{
    @Override
    public void run() {
        System.out.println("线程被运行了");
    }
}

每个线程的逻辑,都是在run方法当中运行的。通过调用start方法来执行线程。start方法只能被调用一次。

3.线程名的创建和获取

Thread t = new Thread(new MyThread2(),"mythreadName2");
t.setName("myNewThreadName2");
t.start();

线程的线程名,需要在启动之前就确定好。

每个线程都有一个默认的名字:Thread-N,N是线程建立的顺序,是一个不重复的正整数

4.获取线程名的方式

在每一个线程的线程栈当中,调用Thread.currentThread().getName()获取线程名

package com.mine.demo01;
public class Main3 {
    public static void main(String[] args) {
        System.out.println("主线程:"+Thread.currentThread().getName());
        Thread t = new Thread(new MyThread3());
        t.start();
    }
}
class MyThread3 implements Runnable {
    @Override
    public void run() {
        System.out.println("子线程:" + Thread.currentThread().getName());
    }
}

结果:
主线程:main
子线程:Thread-0

三、线程调度

线程之间传参,共享数据

synchronized:同一时刻,只有一个线程可以调用该方法。

package com.mine.demo01;
public class Main4 {
    public static void main(String[] args) {
        Test t = new Test();
        for (int i = 0 ; i < 1000 ; i ++){
            new Thread(new Thread1(t)).start();
        }
    }
}
//线程数据的共享和传参
class Test{
    private int i = 0;
    //为了保证数据安全,需要用到同步锁
    public synchronized int getI() {
        return ++i;
    }
}
class Thread1 implements Runnable{
    private Test t;
    public Thread1(Test t){
        this.t = t;
    }
    @Override
    public void run() {
        System.out.println(t.getI());
    }
}
  • 一个正常线程的生命周期

1.新建线程(1生命形态)
2.启动线程
3.就绪状态(2就绪形态)

4.等待系统分配资源
5.运行状态(3运行形态)
6.死亡状态(4死亡形态)

1.等待

等待的构成:
1.新建
2.就绪
3.运行
4.持锁(先到就绪状态---->运行)
5.等待
6.释放锁
7.时间到或者被唤醒
8.持锁(先到就绪状态---->运行)
9.执行等待后续
10.死亡

注意:等待必须在同步环境当中,如果不在同步环境中,会报如下异常

java.lang.RuntimeException: java.lang.IllegalMonitorStateException
	at com.qf.demo01.Test.getI(Main4.java:18)
	at com.qf.demo01.Thread1.run(Main4.java:30)
	at java.lang.Thread.run(Thread.java:745)
package com.mine.demo01;

public class Main4 {
    public static void main(String[] args) {
        Test t = new Test();
        for (int i = 0; i < 3; i++) {
            new Thread(new Thread1(t)).start();
        }
    }
}

class Test {
    private int i = 0;

    public synchronized int getI() {
        try {
            System.out.println("等待开始前:" + Thread.currentThread().getName());
            this.wait(3000);
            System.out.println("等待开始后:" + Thread.currentThread().getName());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return ++i;
    }
}

class Thread1 implements Runnable {
    private Test t;

    public Thread1(Test t) {
        this.t = t;
    }

    @Override
    public void run() {
        System.out.println(t.getI());
    }
}

运行结果:

等待开始前:Thread-0
等待开始前:Thread-2
等待开始前:Thread-1
等待开始后:Thread-2
1
等待开始后:Thread-1
2
等待开始后:Thread-0
3

注意:只要是线程进入阻塞,那么他一定回先回到就绪状态

2.休眠

package com.mine.demo05;

public class Main {
    private synchronized static void test(){
        try {
            System.out.println("休眠前:"+Thread.currentThread().getName());
            Thread.sleep(3000);
            System.out.println("休眠后:"+Thread.currentThread().getName());
        }catch (InterruptedException e){
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) {
        for (int i = 0;i < 2 ;i++){
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    test();
                }
            });
            t.start();
        }
    }
}

结果:
休眠前:Thread-0
休眠后:Thread-0
休眠前:Thread-1
休眠后:Thread-1

注意:休眠不释放锁,休眠不可被唤醒,必须等到休眠时间结束

3.优先级(了解)

Thread t = new MyThread();
t.setPriority(5);
t.start();

4.让步(了解)

Thread.yield();//作用仅仅是让当前正在运行的线程回到就绪状态

5.线程的join(生命周期和sleep是一样的)

package com.mine.demo06;

public class Main {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"......");
                try {
                    Thread.sleep(3000);//3s
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"++++++");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        try {
            t1.start();
            t1.join();
            t2.start();
            t2.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

    }
}


结果:
Thread-0......
Thread-1++++++

6.守护线程

用户线程:主帅,用户线程只要在运行,守护线程可以一直运行下去,除非守护线程自行死亡

守护线程:卫兵,只要没有用户线程还在继续执行,那么无论其是否还存活,都将立即死亡

四、同步锁

注意:只要是改数据就,就一定要考虑数据同步问题

  • 对象锁
  //同步方法
    public synchronized void getI() {
        System.out.println(Thread.currentThread().getName()+"持锁");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
public void test(){
        System.out.println(Thread.currentThread().getName()+" "+i);
        synchronized (this){//同步代码块
            System.out.println(Thread.currentThread().getName()+"持锁");
            try {
                Thread.sleep(3000);
                i++;
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println(i);
    }

基于对象的锁,必须是多线程共享的同一个对象

 //基于对象的锁
        private A a = new A();
        public void test(){
            // A a = new A();
            System.out.println(Thread.currentThread().getName()+" "+i);
            synchronized(a){
                System.out.println(Thread.currentThread().getName()+"持锁");
                try {
                    Thread.sleep(3000);
                    i++;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(i);
        }
  • 类锁
package com.mine.demo02;
public class Main2 {
    public static void main(String[] args) {
        for(int i = 0 ; i < 3 ; i ++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    C.fun1();
                }
            }).start();
        }
    }
}
class C{
    public synchronized static void fun1(){
        try {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
public static void fun2(){
	synchronized(C.class){
		try {
			System.out.println(Thread.currentThread().getName());
			Thread.sleep(3000);
		} catch (InterruptedException e) {
				throw new RuntimeException(e);
		}
    }
}
  • 死锁
package com.mine.demo02;
public class Main1 {
    public static void main(String[] args) {
        Test t = new Test();
        new Thread(new Thread1(t)).start();
        new Thread(new Thread2(t)).start();
    }
}
class Test{
    private A a = new A();
    private B b = new B();
    public void test1(){
        synchronized(b){
            System.out.println(Thread.currentThread().getName()+"持b锁");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized(a){
                System.out.println(Thread.currentThread().getName()+"持a锁");
            }
        }
    }
    public void test2(){
        synchronized(a){
            System.out.println(Thread.currentThread().getName()+"持a锁");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized(b){
                System.out.println(Thread.currentThread().getName()+"持b锁");
            }
        }
    }
}
class Thread1 implements Runnable{
    private Test t;
    public Thread1(Test t){
        this.t = t;
    }
    @Override
    public void run() {
        t.test1();
    }
}
class Thread2 implements Runnable{
    private Test t;
    public Thread2(Test t){
        this.t = t;
    }
    @Override
    public void run() {
        t.test2();
    }
}
class A{}
class B{}

注意:
1.静态锁只和静态锁一起用,对象锁只和对象锁一起用,不要混用。
2.持锁顺序保持一致

五、volatile关键字

是一个轻量级的同步锁,因为互斥是同步中最消耗性能的地方,而在读-读/读-写(多读1写),那么这
种情况就只需要考虑可见性。
同步锁的功能包括:互斥,可见

package com.mine.demo02;
public class Person {
    private volatile Integer id;
    private volatile String name;
    public Integer getId() {
        return id;
    }
    public synchronized void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public synchronized void setName(String name) {
        this.name = name;
    }
}

六、生产消费者模型

package com.mine.demo03;
import java.util.Date;
//生成者
public class Producer implements Runnable {
    private final Queue queue;
    public Producer(final Queue queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        for(;;){
            try {
                Thread.sleep(1000);
                this.queue.add(Thread.currentThread().getName() + " " + new
                        Date().getTime());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
}
package com.mine.demo03;
import java.util.Date;
//消费者
public class Consumer implements Runnable{
    private final Queue queue;
    public Consumer(final Queue queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        for(;;){
            try {
                Thread.sleep(1000);
                System.out.println(this.queue.get());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}


package com.qf.demo03;
import java.util.LinkedList;
//队列
public class Queue{
    private static final int MAX_VALUE = 5;
    private LinkedList queue = new LinkedList();
    public synchronized void add(Object obj){
        while(queue.size()==MAX_VALUE){//用while不要用if
            try {
                //满了等待
                System.out.println(Thread.currentThread().getName() + " 满了,等
                        待!!");
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        //没有满,添加
        this.queue.addFirst(obj);
        //每次添加唤醒全部线程
        this.notifyAll();
    }
    public synchronized Object get(){
        while (this.queue.size() == 0){
            //空了等待
            try {
                System.out.println(Thread.currentThread().getName() + " 空了,等
                        待!!");
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        //不为空
        Object obj = this.queue.getLast();
        this.queue.removeLast();
        //每次获取唤醒全部线程
        this.notifyAll();
        return obj;
    }
}
package com.mine.demo03;

import com.oracle.jrockit.jfr.Producer;

import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        Queue queue = new Queue();
        new Thread(new Producer(queue)).start();
        new Thread(new Producer(queue)).start();
        new Thread(new Consumer(queue)).start();
    }
}
posted @ 2023-04-01 17:37  DFshmily  阅读(16)  评论(0编辑  收藏  举报