java16-多线程

程序

  • 为完成特定任务,用某种语言编写的一组指令集合,即一段静态的代码

进程

  • 程序的一次执行过程,或是正在运行的一个程序
  • 有它自身的产生,存在和消亡过程

线程

  • 进程可进一步细化为线程
  • 若一个进程同一时间执行多个线程,就是支持多线程的
  • 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(PC)
  • 一个进程中共享相同的内存单元/内存地址空间=>他们从同一堆中分配对象,可以访问相同的变量和对象
  • 线程之间通信简介和高效,但多个线程共享的系统资源可能会带来安全隐患

并行与并发

  • 并行:多个CPU执行多个任务
  • 并发:一个CPU同时执行多个任务

Thread

  • start()启动当前线程,调用当前线程中的run()
  • run()通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
  • currentThread()静态方法,返回当前代码的线程
  • getName()获取当前线程的名字
  • setName()设置当前线程的名字
  • yield()释放当前CPU的执行权
  • join()在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完了以后,线程a才结束阻塞状态
  • sleep()让当前线程睡眠一段时间
  • isAlice()查看线程是否已结束
public class ThreadMethods {
    
    
    public static void main(String[] args) {
    
    
        ThreadMethod1 t1=new ThreadMethod1();
        t1.setName("线程一");
        t1.start();
        // 主线程命名
        Thread.currentThread().setName("主线程");
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
            if(i==20){
    
    
                try {
    
    
                    t1.join();
                } catch (InterruptedException e) {
    
    
                    throw new RuntimeException(e);
                }
            }

        }
        System.out.println(t1.isAlive());
    }
}
class ThreadMethod1 extends Thread{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(Thread.currentThread().getName()+":"+i);
            }

            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }

        }
    }
}

多线程创建

继承于Thread类

  1. 创建一个继承与Thread的子类
  2. 重写Thread类的run()方法
  3. 创建Thread类的子类的对象
  4. 通过此对象调用start()
  5. 不能直接调用run()方法
  6. 不可以让已经start()的线程再次执行
class MyThread extends Thread{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(i);
            }

        }
    }
}
public class ThreadTest {
    
    
    public static void main(String[] args) {
    
    
        MyThread t1 = new MyThread();
        t1.start();
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(i+"****main****");
            }

        }
    }
}

实现Runnable接口

  1. 创建一个实现了Runnable接口的类
  2. 实现类去实现Runnable中的抽象方法run()
  3. 创建实现类的对象
  4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
  5. 通过Thread类调用start()
public class ThreadRunnable {
    
    
    public static void main(String[] args) {
    
    
        Runnable1 r1=new Runnable1();
        Thread t1=new Thread(r1);
        t1.setName("线程1");
        t1.start();
        Thread t2=new Thread(r1);
        t2.setName("线程2");
        t2.start();
    }
}
class Runnable1 implements Runnable{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

Runnable接口优点

  1. 实现方式没有类的单继承性的局限性
  2. 实现方式更适合处理多个线程共享数据的情况

Callable接口

  1. 实现Callable的实现类
  2. 实现call()方法,把此线程需要执行的方法声明在call()中
  3. 创建Callable接口实现类的对象
  4. 将Callable接口实现类的对象作为参数传递到FutureTask对象中
  5. 将FutureTask的对象作为参数传递到Thread中,调用start()方法启动线程
  6. 获取Callable返回值
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableTest {
    
    
    public static void main(String[] args) {
    
    
        Test t1=new Test();
        FutureTask futureTask = new FutureTask(t1);
        new Thread(futureTask).start();
        try {
    
    
            Object o1=futureTask.get();
            System.out.println(o1);
        } catch (InterruptedException e) {
    
    
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}
class Test implements Callable {
    
    
    @Override
    public Object call() throws Exception {
    
    
        int sum=0;
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(i);
                sum+=i;
            }

        }
        return  sum;
    }
}

与Runnable接口相比优点

  1. 可以在run()方法中有返回值
  2. 可以抛出异常
  3. 支持泛型返回值
  4. 需要借组FultureTask类,比如获取返回结果

线程池

  1. 提前创好多个线程,放入线程池中,使用时直接获取,使用完放回池中
  2. 可以避免重复创建销毁,实现重复利用
  3. 提供指定数量线程池
  4. 执行指定线程操作,需要提供实现Runnable或者Callable接口实现类的对象
  5. 关闭线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoll {
    
    
    public static void main(String[] args) {
    
    
        ExecutorService service = Executors.newFixedThreadPool(10);
        NumThread1 n1=new NumThread1();
        NumThread2 n2=new NumThread2();
        service.execute(n1); // 适合Runnable
        service.execute(n2);
        // service.submit(); // 适合Callable
        // 关闭
        service.shutdown();
    }
}
class NumThread1 implements Runnable{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(Thread.currentThread().getName()+"名字为"+i);
            }
        }
    }
}
class NumThread2 implements Runnable{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2!=0){
    
    
                System.out.println(Thread.currentThread().getName()+"名字为"+i);
            }
        }
    }
}

线程优先级

  • 优先级
    • MAX_PRIORITY:10
    • NORM_PRIORITY:5
    • MIN_PRIORITY:1
  • 设置
    • getPriority()获取线程优先级
    • setPriority(int p)设置线程优先级
  • 设置优先级后并否完全安全优先级来,只能说优先级大的线程比优先级小的线程执行概率大

线程生命周期

  • 新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
  • 就绪:处于新建状态被start()后,将进入线程队列等待CPU时间片,此时他已经具有运行的条件,只是没有分配到CPU资源
  • 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()定义了线程的操作和功能
  • 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的执行,进入阻塞状态
    • sleep(long time)睡眠
    • join()
    • 等待同步锁
    • wait()
    • suspend()
  • 死亡:线程完成了它的全部工作或线程被提前强制性的中止或出现异常导致结束
    • 执行完run()
    • 调用线程的stop()
    • 出现Error/Exception,并且没处理

线程安全问题

同步代码块

  1. 操作共享数据的代码,即为需要同步的代码
  2. 共享数据:多个线程共同操作的变量
  3. 同步监视器:俗称锁
    1. 任何一个类的对象都可以称为锁
    2. 同一个线程用同一个锁
  4. synchronized (同步监视器){ 共享操作 }
public class WindowTest2 {
    
    
    public static void main(String[] args) {
    
    
        Window2 w1=new Window2();
        Thread t1=new Thread(w1);
        Thread t2=new Thread(w1);
        Thread t3=new Thread(w1);
        t1.start();
        t2.start();
        t3.start();
    }
}
class Window2 implements Runnable{
    
    
    public int ticket=100;
    Object o1=new Object();
    @Override
    public void run() {
    
    
        while (true){
    
    
            synchronized (o1) {
    
    
                if (ticket > 0) {
    
    
                    try {
    
    
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
    
    
                        throw new RuntimeException(e);
                    }
                    System.out.println("买票,票号:" + ticket);
                    ticket--;
                } else {
    
    
                    break;
                }
            }
        }
    }
}

同步方法

  1. 如果操作共享数据的代码完整的声明在一个方法中,我们不妨将方法声明同步的
  2. 非静态的同步方法,同步监视器是:this;静态的同步方法,同步监视器是当前类的本身
public class WindowTest3 {
    
    
    public static void main(String[] args) {
    
    
        Window3 w3=new Window3();
        Thread t1=new Thread(w3);
        Thread t2=new Thread(w3);
        Thread t3=new Thread(w3);
        t1.start();
        t2.start();
        t3.start();
    }
}
class Window3 implements Runnable{
    
    
    private int ticket=100;
    @Override
    public void run() {
    
    
        show();
    }
    public synchronized void show(){
    
    
        while (true){
    
    
            if(ticket>0){
    
    
                try {
    
    
                    Thread.sleep(100);
                } catch (InterruptedException e) {
    
    
                    throw new RuntimeException(e);
                }
                System.out.println("卖票中,票号:"+ticket);
                ticket--;
            }else {
    
    
                break;
            }
        }
    }
}

LOCK锁

  1. java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具
  2. 锁提供了对共享资源的独占访问,每次只能有一个线程对LOCK对象加锁,线程开始访问共享资源之前应先获得LOCK对象
  3. LOCK,它拥有与synchronized相同的并发性和内存语义,在实现线程安全控制中,比较常用,可以显示加锁,释放锁
import java.util.concurrent.locks.ReentrantLock;

public class LockTest {
    
    
    public static void main(String[] args) {
    
    
        Window4 w3=new Window4();
        Thread t1=new Thread(w3);
        Thread t2=new Thread(w3);
        Thread t3=new Thread(w3);
        t1.start();
        t2.start();
        t3.start();
    }

}
class Window4 implements Runnable{
    
    
    public int ticket=100;
    // 实例化lock
    private ReentrantLock lock=new ReentrantLock();
    @Override
    public void run() {
    
    
        while (true){
    
    
            try{
    
    
                // 锁
                lock.lock();
                if(ticket>0){
    
    
                    try {
    
    
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
    
    
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName()+"卖票,票号:"+ticket);
                    ticket--;
                }else {
    
    
                    break;
                }
            }finally {
    
    
                // 解锁
                lock.unlock();
            }
        }
    }
}

线程死锁问题

  1. 不同线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了死锁
  2. 死锁后不会抛异常,程序的线程都处于阻塞状态,无法继续

线程的通信

public class CommunicationTest {
    
    
    public static void main(String[] args) {
    
    
        Number n1=new Number();
        Thread t1=new Thread(n1);
        Thread t2=new Thread(n1);
        t1.start();
        t2.start();
    }
}
class Number implements Runnable{
    
    
    public int num=1;
    @Override
    public synchronized void run() {
    
    
        while (true){
    
    
            // 唤醒线程,只唤醒一个 notofyAll()可以唤醒所有的
            notify();
            try {
    
    
                Thread.sleep(100);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
            if(num<=100){
    
    
                System.out.println(Thread.currentThread().getName()+"打印"+num);
                num++;
            }else {
    
    
                break;
            }
            try {
    
    
                // 让当前线程进入阻塞状态,并释放同步监视器
                wait();
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
    }
}

sleep()和wait()区别

相同点

  1. 一旦执行方法,都可以使得当前线程进入阻塞状态

区别

  1. 两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()
  2. sleep()可以在任何场景下调用,wait()必须在同步代码块或者同步方法中调用
  3. 如果两个方法都在同步代码块或者同步方法中,sleep()不会释放锁,wait()会释放锁

猜你喜欢

转载自blog.csdn.net/weixin_64925940/article/details/126909972