多线程

Posted 杨杨杨杨杨杨杨振

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。

创建和启动线程的方式一

  • 自定义类继承Thread类并重写run方法,然后创建该类的对象调用start方法。

创建和启动线程的方式二

  • 自定义类实现Runnable接口并重写run方法,创建该类的对象作为实参来构造Thread类型的对象,然后使用Thread类型的对象调用start方法。
public static void main(String[] args) {
    new Thread(){
        @Override
        public void run() {
            System.out.println("t1");
        }
    }.start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("t2");
        }
    }).start();

    new Thread(()->{
        System.out.println("t3");
    }).start();
}

1.继承方式管理线程编号和名称

public class ThreadIdNameTest extends Thread{

    public ThreadIdNameTest(String sun) {
        //调用父类的构造方法
        super(sun);
    }

    @Override
    public void run() {
        System.out.println("子线程的ID:" + this.getId());
        System.out.println("子线程的名称:" + getName());
    }

    public static void main(String[] args) {
        ThreadIdNameTest t1 = new ThreadIdNameTest("sun");
        t1.start();

        System.out.println("主线程的ID:" + Thread.currentThread().getId());
        System.out.println("主线程的名称:" + Thread.currentThread().getName());
    }
}

===========
主线程的ID:1
子线程的名称:sun
主线程的名称:main

2.接口的方式管理线程编号和名称

public class MainTest {

    public static void main(String[] args) {

        new Thread(()->{
            System.out.println("子线程的ID:" + Thread.currentThread().getId());
            System.out.println("子线程的名称:" + Thread.currentThread().getName());
            Thread.currentThread().setName("tttt");
            System.out.println("子线程的名称:" + Thread.currentThread().getName());
        }).start();

        System.out.println("主线程的ID:" + Thread.currentThread().getId());
        System.out.println("主线程的名称:" + Thread.currentThread().getName());
    }
}
===========
子线程的ID:14
主线程的ID:1
子线程的名称:Thread-0
主线程的名称:main
子线程的名称:tttt

3.常用的方法

  • Sleep

public class ThreadSleepTest extends Thread {

    private boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            LocalDateTime now = LocalDateTime.now();
            String time = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:ss:mm"));
            System.out.println(time);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ThreadSleepTest t1 = new ThreadSleepTest();

        t1.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t1.flag = false;
    }
}
  • 守护线程

public class ThreadDaemonTest extends Thread{

    @Override
    public void run() {
        //默认不是守护线程
        //当子线程不是守护线程时,虽然主线程先结束了,但是子线程依然会继续执行,直到打印所有数据为止
        //当子线程是守护时,当主线程结束后,则子线程随之结束
        for(int i = 0;i < 50;i++){
            System.out.println("子线程:i = "+ i);
        }
    }

    public static void main(String[] args) {

        ThreadDaemonTest t1 = new ThreadDaemonTest();
        //必须在线程启动之前设置子线程为守护线程
        t1.setDaemon(true);
        t1.start();

        for(int i = 0;i < 20;i++){
            System.out.println("主线程:i = "+ i);
        }
    }
}
  • join等待该线程终止

/**
 * @Author 振帅
 * @Date 2021/05/26 21:49
 */
public class ThreadJoinTest {

    public static void main(String[] args) {

        //偶数
        Thread t1 = new Thread(()->{
            for(int i = 0;i < 100;i += 2){
                System.out.println("ti==:" + i);
            }
        });
        //奇数
        Thread t2 = new Thread(()->{
            for(int i = 1;i < 100;i += 2){
                System.out.println("t2:" + i);
            }
        });

        System.out.println("主线程开始");
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程结束");

    }
}

线程同步机制

1.同步代码块实现线程同步方式一

  • synchronized (dm)
/**
 * @Author 振帅
 * @Date 2021/05/26 22:39
 * synchronized 模拟取钱
 */
public class AccountRunnableTest implements Runnable{
    class Demo { }
    //锁
    Demo dm = new Demo();
    //账户余额
    private int balance;

    public AccountRunnableTest(int balance) {
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    @Override
    public void run() {
        System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        //synchronized (new Demo()){ //锁不住
        synchronized (dm){
            //1.模拟从后台查询余额
            int temp  = getBalance();
            //2.模拟取款200
            if (temp > 200) {
                System.out.println("正在出钞,请稍后...");
                temp -= 200;
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("请取走你的钞票!");
            } else {
                System.out.println("余额不足");
            }
            //3.更新余额
            setBalance(temp);
        }
    }

    public static void main(String[] args) {
        AccountRunnableTest account = new AccountRunnableTest(1000);
        Thread t1 = new Thread(account);
        Thread t2 = new Thread(account);

        t1.start();
        t2.start();
        System.out.println("主线程开始等待...");
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("最终账户余额为:"+ account.getBalance());
    }
}

2.同步代码块实现线程同步方式二

  • static 修饰锁
/**
 * @Author 振帅
 * @Date 2021/05/26 22:39
 * synchronized 模拟取钱
 */
public class AccountThreadTest extends Thread{
    static class Demo { }
    //锁 属于类层级 所有对象共享一个
    private static Demo  dm = new Demo();
    //账户余额
    private int balance;

    public AccountThreadTest(int balance) {
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    @Override
    public void run() {
        System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        synchronized (dm){
            //1.模拟从后台查询余额
            int temp  = getBalance();
            //2.模拟取款200
            if (temp > 200) {
                System.out.println("正在出钞,请稍后...");
                temp -= 200;
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("请取走你的钞票!");
            } else {
                System.out.println("余额不足");
            }
            //3.更新余额
            setBalance(temp);
        }
    }

    public static void main(String[] args) {
        AccountThreadTest at1 = new AccountThreadTest(1000);
        AccountThreadTest at2 = new AccountThreadTest(1000);

        at1.start();
        at2.start();
        System.out.println("主线程开始等待...");
        try {
            at1.join();
            at2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("at1账户余额为:"+ at1.getBalance());
        System.out.println("at2账户余额为:"+ at2.getBalance());
    }
}

3.同步方法块实现线程同步方式一

  • 使用synchronized锁住整个方法
/**
 * @Author 振帅
 * @Date 2021/05/26 22:39
 * synchronized 模拟取钱
 */
public class AccountRunnableTest implements Runnable{

    //账户余额
    private int balance;

    public AccountRunnableTest(int balance) {
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    @Override
    public synchronized void run() {

        System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        //1.模拟从后台查询余额
        int temp  = getBalance();
        //2.模拟取款200
        if (temp > 200) {
            System.out.println("正在出钞,请稍后...");
            temp -= 200;
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("请取走你的钞票!");
        } else {
            System.out.println("余额不足");
        }
        //3.更新余额
        setBalance(temp);

    }

    public static void main(String[] args) {
        AccountRunnableTest account = new AccountRunnableTest(1000);
        Thread t1 = new Thread(account);
        Thread t2 = new Thread(account);

        t1.start();
        t2.start();
        System.out.println("主线程开始等待...");
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("最终账户余额为:"+ account
        
        .getBalance());
    }
}

该方式等价于:

synchronized(this) { 整个方法体的代码 }

this 代表调用run方法的对象,由源码可知 调用run方法的对象是account

因此 synchronized(this){} 可以锁住

@Override
public void run() {
    synchronized (this){
        System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        //1.模拟从后台查询余额
        int temp  = getBalance();
        //2.模拟取款200
        if (temp > 200) {
            System.out.println("正在出钞,请稍后...");
            temp -= 200;
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("请取走你的钞票!");
        } else {
            System.out.println("余额不足");
        }
        //3.更新余额
        setBalance(temp);
    }
}

4.同步方法块实现线程同步方式二

  • 当我们对一个静态方法加锁,如:
    public synchronized static void xxx(){….}
  • 那么该方法锁的对象是类对象。每个类都有唯一的一个类对象。获取类对象的方式:类名.class。
  • 静态方法与非静态方法同时使用了synchronized后它们之间是非互斥关系的。
  • 原因在于:静态方法锁的是类对象而非静态方法锁的是当前方法所属对象。
/**
 * @Author 振帅
 * @Date 2021/05/26 22:39
 * synchronized
 */
public class AccountThreadTest extends Thread{
    //账户余额
    private int balance;

    public AccountThreadTest(int balance) {
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    public synchronized static void test(){
        System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        System.out.println("正在出钞,请稍后...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("请取走你的钞票!");
    }

    @Override
    public void run() {
        test();
    }

    public static void main(String[] args) {
        AccountThreadTest at1 = new AccountThreadTest(1000);
        AccountThreadTest at2 = new AccountThreadTest(1000);

        at1.start();
        at2.start();
        System.out.println("主线程开始等待...");
        try {
            at1.join();
            at2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("at1账户余额为:"+ at1.getBalance());
        System.out.println("at2账户余额为:"+ at2.getBalance());
    }
}

等价于

public static void test(){
    synchronized(AccountThreadTest.class){
        System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        System.out.println("正在出钞,请稍后...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("请取走你的钞票!");
    }
}

5.使用Lock(锁)实现线程同步

  • 从Java5开始提供了更强大的线程同步机制—使用显式定义的同步锁对象来实现。
  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
  • 该接口的主要实现类是ReentrantLock类,该类拥有与synchronized相同的并发性,在以后的线程安全控制中,经常使用ReentrantLock类显式加锁和释放锁。

与synchronized方式的比较

  • Lock是显式锁,需要手动实现开启和关闭操作,而synchronized是隐式锁,执行锁定代码后自动
    释放。
  • Lock只有同步代码块方式的锁,而synchronized有同步代码块方式和同步方法两种锁。
  • 使用Lock锁方式时,Java虚拟机将花费较少的时间来调度线程,因此性能更好。
/**
 * @Author 振帅
 * @Date 2021/05/26 22:39
 * ReentrantLock 模拟取钱
 */
public class AccountRunnableTest implements Runnable{

    //账户余额
    private int balance;

    //准备锁
    private ReentrantLock lock = new ReentrantLock();


    public AccountRunnableTest(int balance) {
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    @Override
    public void run() {
        //开始加锁
        lock.lock();
        System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        //1.模拟从后台查询余额
        int temp  = getBalance();
        //2.模拟取款200
        if (temp > 200) {
            System.out.println("正在出钞,请稍后...");
            temp -= 200;
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("请取走你的钞票!");
        } else {
            System.out.println("余额不足");
        }
        //3.更新余额
        setBalance(temp);
        //解锁
        lock.unlock();

    }

    public static void main(String[] args) {
        AccountRunnableTest account = new AccountRunnableTest(1000);
        Thread t1 = new Thread(account);
        Thread t2 = new Thread(account);

        t1.start();
        t2.start();
        System.out.println("主线程开始等待...");
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("最终账户余额为:"+ account.getBalance());
    }
}

线程通信

1.Object类常用的方法

方法声明功能介绍
void wait()用于使得线程进入等待状态,直到其它线程调用notify()或notifyAll()方 法
void wait(long timeout)用于进入等待状态,直到其它线程调用方法或参数指定的毫秒数已经过去为止
void notify()用于唤醒等待的单个线程
void notifyAll()用于唤醒等待的所有线程
/**
 * @Author 振帅
 * @Date 2021/05/26 23:53
 * 线程通信 两个线程交替打印1-100
 */
public class ThreadCommunicateTest implements Runnable {

    private int cnt = 1;

    @Override
    public void run() {
        while (true) {
            synchronized (this){
                //用于唤醒等待的单个线程 写在锁里面
                notify();
                if(cnt <= 100){
                    System.out.println("线程:"+ Thread.currentThread().getName() +"中的cnt:= " +cnt);
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    cnt++;
                    try {
                        //用于使得线程进入等待状态,直到其它线程调用notify()或notifyAll()方法 写在锁里面
                        //当前线程打印完一个数 为了防止继续打印 调用wait()
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    break;
                }
            }

        }
    }

    public static void main(String[] args) {
        ThreadCommunicateTest tc = new ThreadCommunicateTest();
        Thread t1 = new Thread(tc);
        Thread t2 = new Thread(tc);
        t1.start();
        t2.start();
    }
}

2.生产者消费者模型实现

仓库类

/**
 * @Author 振帅
 * @Date 2021/05/27 0:20
 * 仓库类
 */
public class StoreHouse {

    //记录产品的数量
    private int cnt = 0;
    
    //生产方法
    public synchronized void produceProduct(){
        notify();
        if (cnt < 10) {
            System.out.println("线程"+Thread.currentThread().getName()+"正在生产第"+(cnt+1)+"个产品");
            cnt++;
        } else {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
     //消费方法
    public synchronized void consumerProduct(){
        notify();
        if (cnt > 0) {
            System.out.println("线程"+Thread.currentThread().getName()+"正在消费第"+(cnt)+"个产品");
            cnt--;
        } else {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

生产者

/**
 * @Author 振帅
 * @Date 2021/05/27 0:22
 * 生产者
 */
public class ProduceThread extends Thread{

    //声明一个仓库类型的引用作为成员变量,为了能调用仓库类中的生产方法 合成复用原则
    private StoreHouse storeHouse;

    //为了确保两个线程共用一个仓库
    public ProduceThread(StoreHouse storeHouse){
        this.storeHouse = storeHouse;
    }

    @Override
    public void run() {
        while (true) {
            storeHouse.produceProduct();
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

消费者

/**
 * @Author 振帅
 * @Date 2021/05/27 0:22
 * 消费者
 */
public class ConsumerThread extends Thread{

    //声明一个仓库类型的引用作为成员变量,为了能调用仓库类中的生产方法 合成复用原则
    private StoreHouse storeHouse;

    //为了确保两个线程共用一个仓库
    public ConsumerThread(StoreHouse storeHouse){
        this.storeHouse = storeHouse;
    }

    @Override
    public void run() {
        while (true) {
            storeHouse.consumerProduct();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试类

/**
 * @Author 振帅
 * @Date 2021/05/27 0:27
 */
public class StoreHouseTest {

    public static void main(String[] args) {
        //创建仓库类对象
        StoreHouse storeHouse = new StoreHouse();
        ProduceThread t1 = new ProduceThread(storeHouse);
        ConsumerThread t2 = new ConsumerThread(storeHouse);
        t1.start();
        t2.start();
    }
}

创建和启动线程的方式三

  • 实现Callable接口
  • FutureTask类 extends RunnableFuture extends Runnable
/**
 * @Author 振帅
 * @Date 2021/05/27 0:44
 */
public class ThreadCallableTest implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        //计算1-10000之间累加和
        int sum = 0;
        for (int i = 1;i <= 1000; i++){
            sum = sum+i;
        }
        System.out.println("计算的结果为:" + sum);
        return sum;
    }

    public static void main(String[] args) {
        ThreadCallableTest tct = new ThreadCallableTest();
        //用于描述可取消的异步计算,该类提供了Future接口的基本实
        //现,包括启动和取消计算、查询计算是否完成以及检索计算结果的方法,也可以用于获取方法调用
        //后的返回结果
        FutureTask<Integer> task = new FutureTask<Integer>(tct);
        Thread th = new Thread(task);
        th.start();
        Integer result = null;
        try {
            //获取call方法计算的结果
            result = task.get();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("线程处理结果:" + result);

    }
}

创建和启动线程的方式四

  • Executors类

工具类和线程池的工厂类,可以创建并返回不同类型的线程池,常用方法如下:

方法声明功能介绍
static ExecutorService newCachedThreadPool()创建一个可根据需要创建新线程的线程池
static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池
static ExecutorService newSingleThreadExecutor()创建一个只有一个线程的线程池
  • ExecutorService接口

真正的线程池接口,主要实现类是ThreadPoolExecutor,常用方法如下:

方法声明功能介绍
void execute(Runnable command)执行任务和命令,通常用于执行Runnable
Future submit(Callable task)执行任务和命令,通常用于执行Callable
void shutdown()启动有序关闭
public class ThreadPoolTest {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        ThreadCallableTest threadCallableTest = new ThreadCallableTest();
        Future<Integer> future = executorService.submit(threadCallableTest);
        Integer result = null;
        try {
            result = future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("线程处理结果:" + result);
        executorService.shutdown();

    }
}

以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章

线程学习知识点总结

多个请求是多线程吗

python小白学习记录 多线程爬取ts片段

多线程编程

多线程编程

python多线程