Java 线程

目录

程序、进程、线程

线程和进程的关系

创建线程

继承Thread类的方式

实现Runnable接口的方式

实现Callable接口创建线程

线程的生命周期

多线程

线程同步

解决线程安全问题                                                                                    

synchronized关键字的用法

synchronized和Lock锁的区别

wait()和sleep()区别

死锁

线程通信

程序、进程、线程

程序(program)是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码。

进程(process)就是运行中的程序,是操作系统分配资源的最小单位。

线程(thread)是一个进程内部的最小执行单元(具体要做的一件事),是操作系统进行任务调度的最小单元,隶属于进程,进程可进一步细化为线程。

线程和进程的关系

(1)一个进程可以包含多个线程,一个线程只能属于一个进程,线程不能脱离进程而独立运行;

(2)每一个进程至少包含一个线程(称为主线程);在主线程中开始执行程序,java程序的入口main()方法就是在主线程中被执行的;

(3)在主线程中可以创建并启动其它的线程;

(4)一个进程内的所有线程共享该进程的内存资源

创建线程

继承Thread类的方式

实现Runnable接口的方式

实现Callable接口创建线程

实现Callable接口与使用Runnable相比,Callable功能更强大些;

1.相比run()方法,可以有返回值

2.方法可以抛出异常

3.需要借助FuntureTask类,获取返回结果

(1)接收任务

FuntureTask funturetask = new FuntureTask(任务);

(2)创建线程

Thread thread = new Thread(funturetask);

thread.start();

Integer value = futureTask.get();  //获得线程call方法的返回值

线程的生命周期

图1

多线程

当涉及到多线程编程时,虽然多线程可以带来一些好处,但也存在一些缺点。下面是关于Java中多线程的优缺点:

优点:

  1. 提高程序的效率:多线程可以并行执行多个任务,从而提高程序的执行速度和效率。特别是在一些需要并发处理大量计算任务或IO操作的情况下,多线程可以更好地利用系统资源,提高执行效率。

  2. 提高系统的响应能力:多线程允许程序同时处理多个请求,这可以使系统更快地响应用户的操作。例如,在Web服务器中,多线程可以同时处理多个用户的请求,提高用户的访问速度和系统的并发性能。

  3. 提高资源利用率:多线程可以充分利用CPU的多个核心,使得CPU的利用率达到最大化。通过并行执行多个任务,可以减少CPU的空闲时间,提高资源的利用效率。

  4. 实现更复杂的功能:多线程可以实现一些复杂的功能,如多任务并行处理、并发读写操作、线程间的通信等。它可以帮助实现一些高级功能,如并发搜索算法、数据并行处理、分布式计算等。

缺点:

  1. 编程复杂度高:多线程编程涉及到线程的同步与互斥,需要考虑线程安全性、资源竞争、死锁等问题。编写和调试多线程程序比较复杂,容易出现并发相关的bug。

  2. 资源消耗大:每个线程都需要占用系统资源,包括内存和CPU时间片。当同时运行大量线程时,会消耗更多的系统资源,可能会导致系统的负载增加。

  3. 可能引发并发问题:多线程编程可能会引发一些并发问题,如数据竞争、死锁、活锁等。这些问题需要仔细地设计和思考,确保程序的正确性和稳定性。

  4. 可能引发性能下降:如果线程之间存在频繁的上下文切换,可能会导致性能下降。上下文切换需要花费时间和内存,同时线程间的竞争和同步也会带来一定的开销。在某些情况下,并不是线程越多越好,需要综合考虑来提升性能。

因此,在使用多线程编程时,需要仔细评估和权衡其优点和缺点,选择适当的场景和设计方案,确保程序的正确性和性能。

线程同步

解决线程安全问题                                                                                    

(1)同步代码块:使用 synchronized 同步关键字来修饰代码块,确保同一时刻只能有一个线程访问该代码块
(2)同步方法:使用 synchronized 同步关键字来修饰方法,确保同一时刻只能有一个线程访问该方法
(3)使用锁:Java 提供了内置的锁机制,通过 Lock 接口及其实现类,如 ReentrantLock 来控制多个线程对共享资源的访问

synchronized关键字的用法

(1)需要注意锁的作用范围,使用synchronized修饰一段代码块时,要注意多个线程对应的是同一个对象;synchronized修饰方法时,要注意方法是静态还是非静态,静态方法的锁对象是该类的Class对象,而非静态方法的锁对象默认是this
(2)使用synchronized时要注意避免出现死锁的情况,为了避免死锁,要合理规划锁的获取顺序,并避免长时间持有锁

synchronized和Lock锁的区别

(1)synchronized是Java语言内置的关键字,在方法上或代码块中使用;而Lock是一个接口,在Java的并发包(java.util.concurrent)中定义。
(2)synchronized关键字是隐式获取锁的,当线程进入同步代码块时自动获取锁,并在代码块执行完毕或发生异常时自动释放锁。而Lock锁需要显式地获取和释放锁,使用lock()方法获取锁,使用unlock()方法释放锁
(3)synchronized关键字只有一种锁,即对象锁,每个对象都有一个锁与之关联。而Lock锁提供了多种锁的实现,如ReentrantLock等

wait()和sleep()区别

(1)wait()是Object类中的方法,而sleep()是Thread类中的静态方法
(2)wait()方法必须在同步代码块或同步方法中使用,而sleep()方法可以在任何地方使用
(3)调用wait()方法会释放当前线程持有的锁,使得其他等待线程可以获取到该锁,而调用sleep()方法则不会释放锁
(4)调用wait()方法后,需要其他线程调用相同对象上的notify()或notifyAll()方法来唤醒等待的线程;而调用sleep()方法可以通过指定时间长度或者被中断来唤醒

引入案例:

public class Ticket implements Runnable {
    int num = 10;

    public void run() {
        while (true) {
            if (num == 0) {
                break;
            }
            this.printTicket();
        }
    }

    public synchronized void printTicket() {
        if (num > 0) {
            try {
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + num);
            num--;
        }
    }
}
public class Ticket extends Thread {
    static int num = 10;

    public void run() {
        while (true) {
            if (num == 0) {
                break;
            }
            this.printTicket();
        }
    }

    public static synchronized void printTicket() {
        if (num > 0) {
            try {
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + num);
            num--;
        }
    }
}

死锁

产生死锁的四个必要条件

互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
循环等待条件:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。

 引入一个案例:

public class DieLock extends Thread{
    static final Object objectA = new Object();
    static final Object objectB = new Object();
    boolean flag;

    public DieLock(boolean flag) {
        this.flag = flag;
    }

    public void run() {
        if(flag) {
            synchronized (objectA) {
                System.out.println("if objectA");
                synchronized (objectB) {
                    System.out.println("if objectB");
                }
            }
        } else {
            synchronized (objectB) {
                System.out.println("else objectB");
                synchronized (objectA) {
                    System.out.println("else objectA");
                }
            }
        }
    }
}

线程通信

引入案例:

public class PrintNumThread extends Thread{
    static int num = 1;
    static Object object = new Object();
    public void run() {
        while (true) {
            synchronized (object) {
                object.notify();
                System.out.println(Thread.currentThread().getName() + num);
                num++;
                if(num>=100) {
                    break;
                }
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}
//柜台
public class Counter {
    int num = 3; //共享数据

    //生产者添加商品
    public synchronized void add() {
        if(num == 0) {
            num = 3;
            this.notify();
            System.out.println("生产者添加了三个商品");
        }
        else {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //消费者买走商品
    public synchronized void sub() {
        if(num > 0) {
            num--;
            this.notify();
            System.out.println("消费者买走一个商品");
        }
        else {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//消费者线程
public class CustomerThread extends Thread{
    Counter counter;//共享数据

    public CustomerThread(Counter counter) {
        this.counter = counter;
    }

    public void run() {
        while (true) {
            try {
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            counter.sub();
        }
    }
}
//生产者线程
public class ProductorThread extends Thread{
    Counter counter;//共享数据

    public ProductorThread(Counter counter) {
        this.counter = counter;
    }

    public void run() {
        while (true) {
            try {
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            counter.add();
        }
    }
}

引入案例:

public class SumTask implements Callable<Integer> {
    int sum = 0;

    public Integer call() throws InterruptedException {
        Thread.currentThread().sleep(1000);
        for (int i = 1; i <= 10; i++) {
            sum += i;
        }
        return sum;
    }
}
public class Test {
    public static void main(String[] args) {
        SumTask sumTask = new SumTask();
        FutureTask futureTask = new FutureTask(sumTask);
        Thread thread = new Thread(futureTask);
        thread.start();
        try {
            System.out.println(futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值