为完成某种特定任务,用某种语言编写的一组指令的集合,它是一段静态的代码
是程序的一次执行过程,或是正在运行的一个程序,动态的过程
创建-存在-消亡
是进程的一部分,是程序内部的一条执行路径
每个线程拥有独立的运行栈和程序计数器
进程是程序运行和资源分配的基本单位;
线程是 CPU调度 和 分派 的能够独立运行的基本单位;
创建线程方式 1:
继承 Thread 类,重写 run()方法;
- 不能直接调用
run()
方法启动线程,这样做只是调用了一个类的普通方法;- 不可以再让已经
start()
的线程,再次调用start()
方法,会报异常IllegalThreadStateException
。
方法名 | 方法作用 |
---|---|
void start() | 此线程开始执行,Java虚拟机调用此线程的run方法 |
void run() | 线程的执行内容 |
String getName() | 返回此线程的名称 |
void setName(String name) | 设置此线程的名称为参数 name |
int getPriority() | 返回此线程的优先级 |
void setPriority(int newPriority) | 更改线程优先等级 |
static Thread currentThread() | 返回对当前正在执行的线程对象的引用。 |
static void sleep(long millis) | 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行) |
void yield() | 当前线程释放 CPU 的执行权(也可能立马又继续 执行了) |
void join() | 在线程 A 中调用线程 B.join(),此时线程 A 进入阻塞 状态,直到线程 B 完全执行完以后,线程 A 才结束阻塞状态 |
void stop()(已弃用) | 停止线 |
时间片:抢占式的,根据线程优先级抢占 CPU 的执行权,高优先级的更容易获得;同优先级线程,先进先出队列。
使用方法:
start()
方法前调用 thread.setDaemon(true)
可以将一个 用户线程 变成一个 守护线程。synchronized(同步监视器){
// 需要被同步的代码:线程对共享数据操作的语句
}
锁
。任何一个类的对象,都可以充当锁;但是多个线程必须共用同一把锁;当前类名.class
作为同步监视器;this
充当同步监视器如果操作共享数据的代码完整的声明在一个方法中,不妨使用声明整个方法同步
this
;当前类本身
;java.util.concurrent.locks.lock
实现类:
ReentrantLock、
ReentrantReadWriteLock.ReaLock、
ReentrantReadWriteLock.WriteLock
JDK 5.0 开始,java 提供了更强大的线程同步机制,:通过显式定义同步锁对象来实现。同步锁使用Lock
对象充当;
java.util.concurrent.locks.Lock
接口是控制多个线程对共享资源进行访问的工具;
锁
提供了对共享资源的独立访问,每次只能有一个线程对Lock
对象加锁,线程开始访问共享资源之前应先获得Lock
对象;
ReentrantLock
类实现了Lock
,它拥有与Synchronized
相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock
,可以显式加锁,释放锁。
实现
公平锁:先来后到
非公平锁:可以插队(默认)
wait()
:一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器;
notify()
:一旦执行此方法,就会唤醒被 wait 的一个线程,如果有多个线程被wait,就唤醒优先级高的;
notifyAll()
:一旦执行此方法,就会唤醒所有被 wait 的线程。
说明:
wait()
、notify()
、notifyAll()
三个方法必须使用在同步代码块或同步方法中;
wait()
、notify()
、notifyAll()
三个方法的调用者必须是同步代码块或同步方法中的同步监视器,
否则,会出现IllegalMonitorStateException
异常
生产者(Productor)将产品交给店员(Clerk),而消费者(Customre)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者视图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
Producer(生产者)
// 生产者:控制产品对象
class Producer extends Thread{
private Product product;
public Producer(){
}
public Producer(Product product){
this.product = product;
}
// 生产产品
public void run(){
System.out.println("生产产品================");
// 调用产品方法生产
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
product.production();
}
}
}
Consumer(消费者)
// 消费者:控制产品对象
class Consumer extends Thread{
private Product product;
public Consumer(){
}
public Consumer(Product product){
this.product = product;
}
// 购买产品
public void run(){
System.out.println("购买产品================");
// 调用产品方法出售
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
product.sell();
}
}
}
Product(产品)
// 产品类
class Product{
private int productCount = 0;
private Object sync = new Object();
// 生产产品
public synchronized void production(){
if(productCount < 20){
productCount++;
System.out.println(Thread.currentThread().getName() + "---生产第" + productCount + "件商品");
notify();
} else{
System.out.println(Thread.currentThread().getName() + "---产品数大于 20,wait()执行");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 出售
public synchronized void sell(){
if(productCount > 0){
System.out.println(Thread.currentThread().getName() + "***购买了第" + productCount + "件商品");
productCount--;
notify();
} else{
System.out.println(Thread.currentThread().getName() + "---没有商品了,wait()执行");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行(开始生产、消费):
public class ProductTest {
public static void main(String[] args) {
Product product = new Product();
// 生产线
Producer producer = new Producer(product);
producer.setName("生产者");
// 消费线1
Consumer consumer1 = new Consumer(product);
consumer1.setName("消费者1");
// 消费线2
Consumer consumer2 = new Consumer(product);
consumer2.setName("消费者2");
producer.start();
consumer1.start();
consumer2.start();
}
}
JDK 5.0 新增创建线程方式:
- 实现
Callable
接口;- 使用线程池
与使用 Runnable 相比,Callable 功能更强大
- 相比
run()
方法,可以有返回值;- 方法可以抛出异常;
- 支持泛型的返回值;
- 需要借助
FutureTask
类,比如获取返回结果
Runnable
、Callable
任务的执行结果进行取消、查询是否完成、获取结果等;FutureTask
是 Future
接口的唯一的实现类;FutureTak
同时实现了Runnable
、Future
接口,它既可以作为Runnable
北县城执行,又可以作为Future
得到Callable
的返回值创建一个实现了Callable
接口的实现类,可实现泛型,即为call()方法的返回值类型;
实现 call() 方法,将此线程需要执行的操作声明在 call() 中;
// 1、创建一个实现了 Callable 接口的实现类,可实现泛型
class NumThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + "执行-----");
int sum = 0;
for(int i = 0; i < 100; i++){
if(i % 2 == 0){
sum += i;
}
}
return sum;
}
}
创建Callable
接口实现类的对象;
将Callable
接口实现类的对象作为参数传递到FutureTask
构造器中,创建FutureTask
的对象;
将FutureTask
的对象作为参数传递到Thread
类的构造器中,创建Thread
对象,并调用start()
方法。
获取Callable
接口实现类中call()
方法的返回值。
public class Callable_Test {
public static void main(String[] args) {
// 3、创建`Callable`接口实现类的对象;
NumThread numThread = new NumThread();
// 4、将`Callable`接口实现类的对象作为参数传递到`FutureTask`构造器中,创建`FutureTask`的对象;
FutureTask<Integer> futureTask = new FutureTask<Integer>(numThread);
// 5、将`FutureTask`的对象作为参数传递到`Thread`类的构造器中,创建`Thread`对象,并调用`start()`方法。
Thread thread = new Thread(futureTask);
thread.start();
try {
// 6、获取`Callable`接口实现类中`call()`方法的返回值。
Integer sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对想能影响很大;
思路:梯田创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建、销毁,实现重复利用。
好处:
提高相应速度(减少了创建新线程的时间);
降低资源消耗(重复利用线程池中线程,不需要每次都创建);
便于线程管理
JDK 5.0 起提供了线程池相关的API:
ExecutorService
接口、Executors
工具类
ThreadPoolExecutor
Runnable
;Callable
public class Executors_Test {
public static void main(String[] args) {
// 1、提供指定线程数的线程池,根据 Executors 工具类的源码可知,该方法返回的对象其实是 ThreadPoolExecutor 类
// 因为 ThreadPoolExecutor --> 继承AbstractExecutorService --> 实现ExecutorService
ThreadPoolExecutor service = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
// 获取到 ThreadPoolExecutor 类可以使用更多的方法来管理线程池。如:
service.setCorePoolSize(20);
// service.setKeepAliveTime(long time,TimeUnit unit);
// service.setMaximumPoolSize(30);
// service.setRejectedExecutionHandler(RejectedExecutionHandler handler);
// service.setThreadFactory(ThreadFactory factory);
// 2、执行指定的线程操作,需要提供实现 Runnable 接口或 Callable 接口的实现类
System.out.println(service.getClass());
// 2.1 使用 Runnable 接口实现类
service.execute(new Number_Runnable());
// 2.2 使用 Callable 接口实现类,并有返回值
Future<Integer> future = service.submit(new Number_Callable());
try {
int sum = future.get();
System.out.println("Callable实现类获取返回值" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
// 3、关闭连接池
service.shutdown();
}
}
// 实现 Runnable
class Number_Runnable implements Runnable{
@Override
public void run() {
int sum = 0;
for (int i = 0; i < 100; i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + "--" + i);
sum += i;
}
}
System.out.println("Runnable--100以内偶数和为:" + sum);
}
}
// 实现 Callable并使用泛型控制返回值类型
class Number_Callable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++){
if(i % 2 == 1){
System.out.println(Thread.currentThread().getName() + "--" + i);
sum += i;
}
}
return sum;
}
}
synchronized
是内置的java关键字;Lock
是一个java类;synchronized
机制在执行完相应的同步代码块以后,自动的释放锁;Lock
需要手动启动同步(lock()
),同时结束同步也需要手动的实现(unlock()
)。synchronized
可重入锁,不可以中断,非公平的;Lock
可重入锁,可以判断锁,非公平(可以自己设置)
sleep()
与wait()
的区别sleep()
方法是属于 Thread 类中的;而wait()
方法,则是属于 Object 类的;sleep()
方法可以再任意地方调用;wait()
只能在同步代码块中调用;sleep()
方法使程序暂停执行指定的时间,让出cpu给其他线程,但是它的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。所以在调用sleep()
方法的过程中,线程不会释放对象锁;wait()
方法的时候,线程会放弃对象锁,进入等待此线程的等待锁定池,只有针对此对象调用notify()
或notifyAll()
方法后本线程才进入对象锁定池准备获取对象锁进入运行状态;注意:唤醒后,将在wait()
的地方继续执行。