单CPU系统中: 每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那是因为分时交替运行的时间是非常短的。 多CPU系统中: 则这些可以并发执行的程序便可以分配到多个处理器上(CPU),实现多任务并行执行,即利用每个处理器来处理一个可以并发执行的程序。
根本区别:
进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
进程: 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多
个进程;
线程: 进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可
以有多个线程的,这个应用程序也可以称之为多线程程序。
进程和线程的关系
Java语言提主要供了两种实现线程的方式:
实现步骤:
自定义线程类:
public class MyThread extends Thread {
/**
* 利用继承中的特点 * 将线程名称传递 进行设置
*/
public MyThread(String name) {
super(name);
}
/**
* 重写run方法 * 定义线程要执行的代码
*/
public void run() {
for (int i = 0; i < 20; i++) {
//getName()方法 来自父亲
System.out.println(getName() + i);
}
}
}
测试类:
public class Demo {
public static void main(String[] args) {
System.out.println("这里是main线程");
MyThread mt = new MyThread("小强");
mt.start();//开启了一个新的线程
for (int i = 0; i < 20; i++) {
System.out.println("旺财:"+i);
}
流程图:
定义实现类:
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
定义测试类:
public class Demo {
public static void main(String[] args) {
//创建自定义类对象 线程任务对象
MyRunnable mr = new MyRunnable();
//创建线程对象
Thread t = new Thread(mr, "小强");
t.start();
for (int i = 0; i < 20; i++) {
System.out.println("旺财 " + i);
}
实现了Runable接口的有点:
public class NoNameInnerClassThread {
public static void main(String[] args) {
Runnable r = new Runnable(){
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println("张宇:"+i);
};
new Thread(r).start();
for (int i = 0; i < 20; i++) {
System.out.println("费玉清:"+i);
}
实现类
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("Start to work");
Thread.sleep(5000);
System.out.println("End done");
return "结束";
}
}
FutureTask方式开启新线程
import java.util.concurrent.FutureTask;
public class FutureTaskDemo {
public static void main(String[] args) throws Exception {
FutureTask<String> task = new FutureTask<String>(new MyCallable());
//启动线程
new Thread(task).start();
// 线程等待
if (!task.isDone()) {
System.out.println("等待中。。。");
}
System.out.println("返回结果为:" + task.get());
}
}
执行结果:
线程池(ThreadPool)方式开启新线程
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadPoolDemo {
public static void main(String[] args) {
// 1、创建线程池
ExecutorService pool = Executors.newCachedThreadPool();
// 2、获取返回的结果
Future<String> result = pool.submit(new MyCallable());
// 3、线程等待
if (!result.isDone()){
System.out.println("等待中。。。");
}
try {
// 4、获取返回值
System.out.println(result.get());
} catch (Exception e) {
e.printStackTrace();
}finally {
// 5、关闭线程池
pool.shutdown();
}
}
}
执行结果:
synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。 格式:
synchronized(同步锁){
需要同步操作的代码
}
代码:
public class Ticket implements Runnable {
private int ticket = 100;
Object lock = new Object();
// 执行卖票操作
@Override
public void run() {
// 每个窗口卖票的操作
// 窗口 永远开启
while (true) {
synchronized (lock) {
if (ticket > 0) {//有票 可以卖
// 出票操作
// 使用sleep模拟一下出票时间
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取当前线程对象的名字
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖:" + ticket--);
}
}
}
}
}
同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着 格式:
public synchronized void method(){
可能会产生线程安全问题的代码
}
代码:
public class Ticket implements Runnable {
private int ticket = 100;
//执行卖票操作
@Override
public void run() {
// 每个窗口卖票的操作
// 窗口 永远开启
while (true) {
sellTicket();
}
}
// 锁对象是谁调用这个方法就是谁
// 隐含锁对象就是 this
public synchronized void sellTicket() {
if (ticket > 0) {//有票 可以卖
// 出票操作
// 使用sleep模拟一下出票时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取当前线程对象的名字
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖:" + ticket--);
}
}
}
格式:
public void lock() :加同步锁。
public void unlock() :释放同步锁
代码:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Ticket implements Runnable {
private int ticket = 100;
Lock lock = new ReentrantLock();
//执行卖票操作
@Override
public void run() {
// 每个窗口卖票的操作
// 窗口 永远开启
while (true) {
lock.lock();
if (ticket > 0) {//有票 可以卖
// 出票操作
// 使用sleep模拟一下出票时间
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取当前线程对象的名字
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖:" + ticket--);
} lock.unlock();
}
}
}
线程状态 | 导致状态发生条件 |
---|---|
NEW(新建) | 线程刚被创建,但是并未启动。还没调用start方法。 |
Runnable(可运行) | 线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。 |
Blocked(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。 |
Waiting(无限等待) | 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。 |
TimedWaiting(计时等待) | 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep. |
Teminated(被终止) | 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。 |
list.wait(); 进入到等待状态
list.notify();唤醒获取元素的线程
public Thread() :分配一个新的线程对象。
public Thread(String name) :分配一个指定名字的新的线程对象。
public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字
public String getName() : 获取当前线程名称。
public void start() : 导致此线程开始执行; Java虚拟机调用此线程的run方法。
public void run() : 此线程要执行的任务在此处定义代码。
public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static Thread currentThread() :返回对当前正在执行的线程对象的引用。
getId() :返回该线程的唯一标识符。
方法isAlive()的功能是判断当前线程是否处于活跃状态。处于已经启动且尚未终止。
创建MyThread类
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("run="+this.isAlive());
}
}
运行Test类中方法
public class Test {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
System.out.println("begin =="+myThread.isAlive());
myThread.start();
System.out.println("end =="+myThread.isAlive());
Thread.sleep(1000);
System.out.println("timeOut =="+myThread.isAlive());
}
}
运行结果如图:
区别:
void wait(): | 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。 |
---|---|
void wait(long timeout): | 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。 |
void wait(long timeout, int nanos): | 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。 |
public class WaitSleepDemo {
public static void main(String[] args) {
final Object lock = new Object();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread A is wait to get lock");
// 1、获取锁
synchronized (lock) {
try {
// 2、休眠20ms
Thread.sleep(20);
System.out.println("thread A get lock");
// 3、调用wait,释放锁
lock.wait(1000);
System.out.println("thread A is done");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
方法sleep()的作用是在指定的毫秒数让当前“当前正在执行的线程”休眠(暂停执行)。
锁池(EntryList):
假设线程A已经拥有了某个对象(不是类)的锁,而其它线程B、C想要调用这个对象的某个 synchronized方法(或者块),由于B、C线程在进入对象的synchronized方法(或者块)之前必须先获得该对象锁的拥有权,而恰巧该对象的锁目前正被线程A所占用,此时B、C线程就会被阻塞,进入一个地方去等待锁的释放,这个地方便是该对象的锁池(等待锁的池子)。
等待池(WaitSet):
假设线程A调用了某个对象的wait( )方法,线程A就会释放该对象的锁,同时线程A就进入到了该对象的等待池中,进入到等待池中的线程不会去竞争该对象的锁。除非被 notify( )、notifyAll( ) 唤醒才行。
notify( )和notifyAll( )区别
notifyAll( ): 会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会。 notify( ): 只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会。
唤醒线程:
public class Test {
private static Test test = new Test();
public static void main(String[] args) throws InterruptedException {
// 1、线程等待wait()
Runnable runnable = new Runnable() {
@Override
public void run() {
test.go();
}
};
new Thread(runnable, "AA_01").start();
// 2、唤醒线程notify();
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
test.goDown();
}
}, "notifyAll").start();
}
// 调用线程等待的wait()方法
private synchronized void go() {
System.out.println("start: " + Thread.currentThread().getName());
try {
this.wait();
System.out.println("end: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 调用唤醒线程的notify()方法
private synchronized void goDown() {
System.out.println("start: " + Thread.currentThread().getName());
notifyAll();
System.out.println("end: " + Thread.currentThread().getName());
}
}
当调用Thread.yield( )函数时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器可能会忽略这个暗示。
已经被废弃的:
通过调用Stop( )方法停止线程。
通过调用suspend( )和resume( )方法
目前使用的方法:
public static void main(String[] args) throws InterruptedException {
Runnable r = new Runnable() {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
执行任务
}
System.out.println("000");
}
};
Thread t1 = new Thread(r, "Start");
1、启动线程
t1.start();
Thread.sleep(100);
2、终止线程
t1.interrupt();
}
1.主线程等待 当主线程没有获取到值时,主线程等待子线程完成赋值。、
优点:实现简单, 缺点:
子线程:
public class CycleWait implements Runnable {
private String name;
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
name = "we have date now";
}
}
主线程:
// 主线程
public static void main(String[] args) {
CycleWait cw = new CycleWait();
Thread t = new Thread(cw);
t.start();
// 当辅助线程中name没有值时,主线程等待
while (cw.name == null) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(cw.name);
}
2.使用Thread类的join()阻塞当前线程等待子线程处理完毕
使用Thread里面的join( )方法,替代主线程等待法代码。
优点:更精准控制,代码更简单。
缺点:力不不够细,不能指定等待某个子线程完成再执行。
3.通过Callable接口:通过FutureTask或者线程池实现
子线程:
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
String value = "test";
System.out.println("Ready to work");
Thread.sleep(5000);
System.out.println("task done");
return value;
}
}
使用FutureTask方式:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class FutureTaskDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> task = new FutureTask<String>(new MyCallable());
//启动线程
new Thread(task).start();
// 线程等待
if (!task.isDone()){
System.out.println("wait");
}
System.out.println("task return="+task.get());
}
}
使用线程池方式:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadPoolDemo {
public static void main(String[] args) {
// 1、创建线程池
ExecutorService pool = Executors.newCachedThreadPool();
// 2、获取返回的结果
Future<String> result = pool.submit(new MyCallable());
// 3、线程等待
if (!result.isDone()){
System.out.println("wait ..");
}
try {
// 4、获取返回值
System.out.println(result.get());
} catch (Exception e) {
e.printStackTrace();
}finally {
// 5、关闭线程池
pool.shutdown();
}
}
}
线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
三个优点:
execute和submit区别
执行给定的任务(command-可运行的任务)(无返回值)
void execute(Runnable command)
提交一个 Runnable 任务用于执行,(有返回值)
Future<?> submit(Runnable task)
线程池的结构:
先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务。
当执行当前任务时上一个任务已经完成,会复用执行上一个任务的线程,而不用每次新建线程。
创建线程池:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
使用线程池中线程:
cachedThreadPool.execute(Runnable command);
代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NewCachedThreadPoolTest {
public static void main(String[] args) {
// 创建一个可缓存线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
try {
// sleep可明显看到使用的是线程池里面以前的线程,没有创建新的线程
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
public void run() {
// 打印正在执行的缓存线程信息
System.out.println(Thread.currentThread().getName()
+ "正在被执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
创建一个可重用固定个数的线程池,以共享的无界队列方式来运行这些线程。
定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()
创建线程池:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
使用线程池中线程:
fixedThreadPool.execute(Runnable command);
代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NewFixedThreadPoolTest {
public static void main(String[] args) {
// 1、创建一个可重用固定个数的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
// 2、使用线程池中线程
fixedThreadPool.execute(new Runnable() {
public void run() {
try {
// 打印正在执行的缓存线程信息
System.out.println(Thread.currentThread().getName()
+ "正在被执行");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
创建一个定长线程池,支持定时及周期性任务执行.
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
延迟指定delay
秒数执行:
ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
延迟 initialDelay
秒后每period
秒执行一次
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,
long period,TimeUnit unit);
代码:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class NewScheduledThreadPoolTest {
public static void main(String[] args) {
//创建一个定长线程池,支持定时及周期性任务执行——延迟执行
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
// //1、延迟1秒执行
// scheduledThreadPool.schedule(new Runnable() {
// public void run() {
// System.out.println("延迟1秒执行");
// }
// }, 1, TimeUnit.SECONDS);
//2、延迟1秒后每3秒执行一次
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("延迟1秒后每3秒执行一次");
}
}, 1, 3, TimeUnit.SECONDS);
}
}
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
创建线程池:
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
使用线程池中线程:
singleThreadExecutor.execute(Runnable command);
代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NewSingleThreadExecutorTest {
public static void main(String[] args) {
// 1、创建一个单线程化的线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
// 2、使用线程池中线程
singleThreadExecutor.execute(new Runnable() {
public void run() {
try {
// 结果依次输出,相当于顺序执行各个任务
System.out.println(Thread.currentThread().getName() + "正在被执行,打印的值是:" + index);
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
1、配置类的方式配置线程池,并注入
@Configuration
public class ExecturConfig {
@Bean("taskExector")
public Executor taskExector() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
int i = Runtime.getRuntime().availableProcessors();//获取到服务器的cpu内核
executor.setCorePoolSize(5);//核心池大小
executor.setMaxPoolSize(100);//最大线程数
executor.setQueueCapacity(1000);//队列最大长度
executor.setKeepAliveSeconds(1000);//线程池维护线程所允许的空闲时间
executor.setThreadNamePrefix("tsak-asyn");//线程前缀名称
//配置拒绝策略【线程池对拒绝任务(无线程可用)的处理策略】
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
return executor;
}
}
2、基于xml配置的方式创建
<!-- spring线程池 -->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 核心线程数 -->
<property name="corePoolSize" value="10"/>
<!-- 最大线程数 -->
<property name="maxPoolSize" value="200"/>
<!-- 队列最大长度 >=mainExecutor.maxSize -->
<property name="queueCapacity" value="10"/>
<!-- 线程池维护线程所允许的空闲时间 -->
<property name="keepAliveSeconds" value="20"/>
<!-- 线程池对拒绝任务(无线程可用)的处理策略 -->
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/>
</property>
</bean>
自动注入的方式注入线程池
@Resource(name="taskExecutor")
ThreadPoolTaskExecutor taskExecutor;
// 或者可以直接@Autowried
@AutoWired
ThreadPoolTaskExecutor taskExecutor