博客
关于我
【SE-02】多线程-02
阅读量:323 次
发布时间:2019-03-04

本文共 8234 字,大约阅读时间需要 27 分钟。

Java多线程编程入门与进阶

1. 守护线程

在Java中,守护线程是一种特殊的线程,它不会自动终止,只要主线程还在运行,守护线程也会一直运行。常见的用途包括资源管理、错误处理以及与其他线程协作。

2. 线程的生命周期

线程在其执行过程中会经历几个关键状态:

  • 新生线程:线程被创建,但尚未启动。
  • 运行中:线程正在执行任务。
  • 一致性:线程处于暂停状态,可能被阻塞或等待。
  • 终止:线程完成任务或被强制终止。
  • 线程的状态可以通过调用getState()方法来查看。

    3. 线程的同步

    3.1 问题描述

    当多条语句操作同一个线程共享数据时,一个线程中的多条语句可能只执行了一部分,而另一个线程却参与进来,导致共享数据出现错误。这种现象也就是所谓的“线程安全问题”。

    3.2 解决办法

    为了避免线程安全问题,可以采取以下措施:

  • 同步代码块/同步方法:将需要共享数据的代码放置在一个同步的代码块或同步的方法中。
  • Lock接口:使用Java提供的Lock接口来实现同步,Lock接口支持多级锁和fairness策略。
  • 4. 同步代码块与同步方法

    4.1 同步代码块

    在实现Runnable接口时,共享变量的选择很重要:

  • 任何对象都可以作为共享数据,但通常情况下不会专门为共享数据创建一个对象。
  • 通过this关键字:如果线程类是通过Runnable接口实现的,this关键字可以用来获取共享数据。
  • 通过类对象:如果线程类是通过继承Thread类创建的,那么this关键字不再是唯一的对象,此时需要使用类对象作为共享数据。
  • 4.2 同步方法

    操作共享数据的代码需要被同步:

  • 同步方法:将需要共享数据的代码完全放置在一个方法中,并声明该方法为同步方法。
  • 注意事项
    • 同步方法的默认同步监视器是this对象。
    • 如果通过继承Thread类创建的线程,同步方法需要声明为静态方法,否则会出现线程不安全的问题。
  • 5. 单例模式【懒汉】优化

    5.1 线程不安全的单例模式

    单例模式的实现需要确保在多个线程中只有一个实例被创建。传统的单例模式可能会因为线程竞态条件而导致线程不安全。

    5.2 懒汉式单例模式优化

    通过引入双重锁(Double-Checked Locking)可以实现懒汉式单例模式的线程安全:

    public class Singleton {    private static Singleton instance = null;    private Singleton() {        // 初始化代码    }    public static Singleton getInstance() {        if (instance == null) {            synchronized (Singleton.class) {                if (instance == null) {                    instance = new Singleton();                }            }        }        return instance;    }}

    6. 死锁

    6.1 死锁的定义

    死锁是指两个或多个线程在等待对方完成的操作中都被阻塞,导致无法继续执行的状态。

    6.2 死锁举例

    public class DeadLock {    public static void main(String[] args) {        StringBuffer s1 = new StringBuffer();        StringBuffer s2 = new StringBuffer();        new Thread() {            @Override            public void run() {                synchronized (s1) {                    System.out.println("成功锁住s1");                    s1.append("a");                    try {                        Thread.sleep(200);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    synchronized (s2) {                        s1.append("b");                        s2.append("2");                    }                    System.out.println(s1);                    System.out.println(s2);                }            }        }.start();        new Thread(new Runnable() {            @Override            public void run() {                synchronized (s2) {                    System.out.println("成功锁住s2");                    s1.append("c");                    s2.append("3");                    try {                        Thread.sleep(200);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    synchronized (s1) {                        s1.append("d");                        s2.append("4");                    }                    System.out.println(s1);                    System.out.println(s2);                }            }        }).start();    }}

    7. Lock锁

    7.1 Lock接口的介绍

    Lock接口提供了更加灵活的同步机制,与传统的synchronized关键字相比,具有以下优势:

  • 更灵活的锁管理:可以手动管理锁的状态。
  • 支持fairness策略:可以指定锁的公平性策略。
  • 更高效的锁 fair scheduling:在多线程竞争情况下,Lock接口可以更高效地分配锁。
  • 7.2 Lock接口的代码演示

    class Account {    private double balance;    public Account(double balance) {        this.balance = balance;    }    public void deposit(double money) {        if (money >= 0) {            balance += money;            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(Thread.currentThread().getName() + "存钱,当前余额为:" + balance);        }    }}class AccountThread extends Thread {    private static Lock lock = new ReentrantLock();    private Account account;    public AccountThread(Account account) {        this.account = account;    }    @Override    public void run() {        try {            lock.lock();            for (int i = 0; i < 5; i++) {                account.deposit(1000);            }        } finally {            lock.unlock();        }    }}public class DepositMoneyTest {    public static void main(String[] args) {        Account account = new Account(0);        AccountThread t1 = new AccountThread(account);        AccountThread t2 = new AccountThread(account);        t1.setName("1");        t2.setName("2");        t1.start();        t2.start();    }}

    8. 线程的通信

    8.1 wait与notify

    线程通信是多线程编程中非常重要的一部分,wait()notify()方法提供了线程之间的等待和通知机制。

    8.2 wait与notify的使用

    public class Number implements Runnable {    private int number = 1;    private Object object = new Object();    @Override    public void run() {        while (true) {            synchronized (object) {                object.notify();                if (number <= 100) {                    try {                        Thread.sleep(100);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    System.out.println(Thread.currentThread().getName() + ": " + number);                    number++;                    try {                        object.wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                } else {                    break;                }            }        }    }    public static void main(String[] args) {        Number number = new Number();        Thread thread1 = new Thread(number);        Thread thread2 = new Thread(number);        thread1.setName("1->");        thread2.setName("2->");        thread1.start();        thread2.start();    }}

    9. 生产者与消费者问题

    9.1 生产者线程

    生产者线程的主要任务是向共享数据结构中添加数据。以下是一个简单的生产者线程的实现:

    public class Product {    private Object lock = new Object();    private int goodsCount = 0;    public void produce() {        synchronized (lock) {            System.out.println("生产者线程开始生产");            try {                Thread.sleep(100);            } catch (InterruptedException e) {                e.printStackTrace();            }            goodsCount++;            System.out.println("共有商品:" + goodsCount);            System.out.println("生产者线程结束");        }    }}

    9.2 消费者线程

    消费者线程的主要任务是从共享数据结构中取走数据。以下是一个简单的消费者线程的实现:

    public class Consumer {    private Object lock = new Object();    public void consume() {        synchronized (lock) {            System.out.println("消费者线程开始消费");            try {                Thread.sleep(100);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println("已消费的商品数量为:" + Goods.getGoodsCount());            System.out.println("消费者线程结束");        }    }}

    10. JDK5.0新增创建线程方法

    10.1 Callable接口

    Callable接口允许线程执行一个返回值的任务,并且可以通过FutureTask来实现。

    public class CallableTest implements Callable {    @Override    public Object call() throws Exception {        int sum = 0;        for (int i = 1; i <= 100; i++) {            if (i % 2 == 0) {                System.out.println(i);                sum += i;            }        }        return sum;    }    public static void main(String[] args) throws InterruptedException, ExecutionException {        CallableTest callable = new CallableTest();        FutureTask futureTask = new FutureTask(callable);        new Thread(futureTask).start();        Integer sum = (Integer) futureTask.get();        System.out.println("100内偶数总和为:" + sum);    }}

    10.2 线程池

    线程池提供了一个更高效的方式来执行多线程任务,JDK5.0新增了ExecutorServiceExecutors类。

    public class ThreadPoolTest {    public static void main(String[] args) {        ExecutorService service = Executors.newFixedThreadPool(10);        service.execute(new NumberThread());        service.execute(new NumberThread1());        service.shutdown();    }    static class NumberThread implements Runnable {        @Override        public void run() {            for (int i = 1; i <= 100; i++) {                if (i % 2 == 0) {                    System.out.println(Thread.currentThread().getName() + ":" + i);                }            }        }    }    static class NumberThread1 implements Runnable {        @Override        public void run() {            for (int i = 1; i <= 100; i++) {                if (i % 2 != 0) {                    System.out.println(Thread.currentThread().getName() + ":" + i);                }            }        }    }}

    通过以上内容,可以了解Java多线程编程的核心知识点和实际应用场景。

    转载地址:http://txgh.baihongyu.com/

    你可能感兴趣的文章
    SpringCloud微服务简介
    查看>>
    网页实现微信登录
    查看>>
    vue源码分析(MVVM篇)
    查看>>
    vue源码分析(observe篇)
    查看>>
    ElasticSearch 快照备份和还原
    查看>>
    深入理解Kafka系列(五)--Kafka可靠的数据传递
    查看>>
    React(八)- ReactUI组件库及Redux的使用
    查看>>
    TypeScript系列(一)- TypeScript简介与编译配置
    查看>>
    TypeScript系列文章导航
    查看>>
    TypeScript系列(二)- Webpack打包TS代码
    查看>>
    Windows系统Git安装教程
    查看>>
    hibernate和mybatis的区别
    查看>>
    你为什么从大公司离职,去一家创业公司?
    查看>>
    MyBatis学习总结(三)——优化MyBatis配置文件中的配置
    查看>>
    JavaWeb学习总结(十三)——使用Session防止表单重复提交
    查看>>
    JavaScript学习总结(十一)——Object类详解
    查看>>
    Java中Map的用法详解
    查看>>
    Java注解全面总结
    查看>>
    base64编码字符串和图片的互转
    查看>>
    汉字转为拼音
    查看>>