多线程的概念
多线程就是指程序中包含对个执行单元,可以在一个程序中运行多个不同的线程来执行不同的任务。
在程序需要同时执行多个任务时,就可以用到多线程,提高了CPU的利用率,将复杂的程序结构分为多个线程,独立运行。
但是,线程也是程序,也需要占用内存,线程越多,占用的内存也会越大,会使CPU的开销变大, 多个线程对同一个共享的资源进行访问,会出现线程安全问题。
线程同步
同步锁
● 几个线程之间要排队,一个个对共享资源进行操作,而不是同时进行操作;
● 为了保证数据在方法中被访问时的正确性,在访问时加入锁机制(用synchronized
,ReentrantLock进行实现)
● 确保一个时间点只有一个线程访问共享资源。可以给共享资源加一把锁,哪个
线程获取了这把锁,才有权利访问该共享资源。
(注意:同步锁可以是任何对象,必须唯一,保证多个线程获得是同一个对象(用
来充当锁标记).
)
synchronized上锁
synchronized既可以修饰方法也可以修饰代码块,但是要注意的是,当线程实现的是Runnaable接口可以利用多态,只创建一个对象,所以不需要另外,操作。但是继承的是Thread类,在使用对线程时,要在主进程创建多个对象,所以要在进程里另外创建其他类对象用是synchronized
指向同一个对象。
继承Thread类
进程:
主线程:
(注意: 修饰方法:
1.锁不需要我们提供了,会默认提供锁对象
2.synchronized如果修饰的是非静态的方法,锁对象是this
synchronized如果修饰的是静态方法,锁对象是类的Class对象
一个类只有一个Class对象.)
(同时注意,我们观察运行结果,是有一个线程获取到同步锁,另一个线程就会进入阻塞状态,所有只会有一个线程输出,但是有没有一种情况可以让不同的线程交替输出,这个问题留到下面解决)
实现Runnaable接口
进程:
主线程:(这里的主线程,就用到了多态)
Lock锁
• 从JDK 5.0开始,Java提供了更强大的线程同步机制-通过显式定义同步锁对象
来实现同步。同步锁使用Lock对象充当。
• java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的
工具。
• ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存
语义,在实现线程安全的控制中,可以显式加锁释放锁.
(要注意Lock锁只能修饰代码块)
Runnaable上锁和synchronized上锁的区别
synchronized可以修饰代码块和方法而ReentrantLock只能实现代码块。
ReentrantLock调用时,需要手动的上锁和去除同步锁,而synchronized自动上锁和去除同步锁。
线程通信
概述
线程通讯指的是多个线程通过相互牵制,相互调度,即线程间的相互作用。
涉及三个方法:
.wait一旦执行此方法,当前线程就进入阻塞状态,并释放同步锁对象。
.notify一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,
就唤醒优先级高的那个。
.notifyAll一旦执行此方法,就会唤醒所有被wait的线程。
注意:(.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方
法中。)
(可以清楚地知道,这就是线程交替输出的解决办法,上述问题得到完美解决)
经典的消费者/生产者认问题
生产者(Productor)将产品放在柜台(Counter),而消费者(Customer)从柜台
处取走产品,生产者一次只能生产固定数量的产品(比如:1), 这时柜台中不能
再放产品,此时生产者应停止生产等待消费者拿走产品,此时生产者唤醒消费者来
取走产品,消费者拿走产品后,唤醒生产者,消费者开始等待.
定义Counter类用来实现消费生产功能
定义生产进程:
定义消费进程:
主线程:
(可以清楚的看到不同的线程交替输出)
第三种创建线程的方式:
● 实现Callable接口与使用Runnable相比,Callable功能更强大些.
• 相比run()方法,可以有返回值
• 方法可以抛出异常
• 支持泛型的返回值
• 需要借助FutureTask类,获取返回结果
进程:
主线程: