- 概念
(1)线程:
线程(Thread)是一份独立运行的程序,有自己专用的运行栈。线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。
(2)线程同步:当多个线程同时读写同一份共享资源的时候,可能会引起冲突。线程同步需要牢记以下4点:
a. 线程同步就是线程排队。同步就是排队。线程同步的目的就是避免线程“同步”执行。这可真是个无聊的绕口令。
b. 只有共享资源的读写访问才需要同步。如果不是共享资源,那么就根本没有同步的必要。
c. 只有“变量”才需要同步访问。如果共享的资源是固定不变的,那么就相当于“常量”,线程同时读取常量也不需要同步。至少一个线程修改共享资源,这样的情况下,线程之间就需要同步。
d. 多个线程访问共享资源的代码有可能是同一份代码,也有可能是不同的代码;无论是否执行同一份代码,只要这些线程的代码访问同一份可变的共享资源,这些线程之间就需要同步。
(3)同步调用:同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果
(4)异步调用:当发送了调用的指令,调用者无需等待被调用的方法完全执行完毕;而是继续执行下面的流程。
例如:在某个调用中,需要顺序调用 A, B, C三个过程方法;如他们都是同步调用,则需要将他们都顺序执行完毕之后,方算作过程执行完毕; 如B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是执行开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了。
- 线程创建
三种方式创建线程:
参考地址:https://blog.csdn.net/lihua5419/article/details/80679319
(1)继承Thread方法,重写Thread的run()方法
public class MyThread extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
//super.run();
System.out.println("我是一个线程中的方法");
}
}
开启线程>>>>
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();//开启一个线程方法
doSomething();
//线程下的方法可与上边的线程并发执行,即使线程未执行完毕
}
注:创建线程的目的就是要让线程能独立执行,多线程并发执行,不访问共享资源时互不干扰。
(2)实现Runnable接口
一个是继承Thread类,一个是继承Runable接口,具体写法上一致:
public class RunnableThread implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("我是一个线程方法");
}
}
不同的是开启线程的方式 ,有一点不一样:
public static void main(String[] args) {
Runnable runnable=new RunnableThread();
Thread thread=new Thread(runnable);
thread.start();//开启一个线程方法
//以下的方法可与上边的线程并发执行
doSomething();
}
(3)实现Callable接口和Future创建线程
首先创建Callable接口的实现类CallableThread,实现call()方法,并且有返回值。Callable接口是一个带泛型的接口,泛型的类型就是线程返回值的类型。实现Callable接口中的call()方法,方法的返回类型与泛型的类型相同:
public class CallableThread implements Callable<String>{
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
System.out.println("我是线程中的方法");
return "需要返回的值";
}
}
Callable不能直接获取返回值,需要用FutureTask在外部封装一下再获取返回值:
public static void main(String[] args) {
Callable<String> callable=new CallableThread();
FutureTask<String> futureTask=new FutureTask<String>(callable);
Thread thread=new Thread(futureTask);
thread.start();//开启一个线程方法
//以下的方法可与上边的线程并发执行
doSomething();
try {
futureTask.get();//获取线程返回值
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
- 同步锁 synchronized
参考地址:https://blog.csdn.net/yx0628/article/details/79086511
同步锁的分类大概有两种:
(1)对象锁
当synchronized用来修饰“非静态方法”或修饰“代码块(参数是this)”时,这时synchronized是对象锁
(2)类锁
当synchronized用来修饰“静态方法”或修饰“代码块(参数是xxx.class)”时,这时synchronized是类锁
结论:
a. 当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块,同步方法也是。
b. 当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。同步方法也是。
也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
c. 当类锁和对象锁同时存在时,两种锁互不干扰 - 注解创建线程
参考地址:https://www.cnblogs.com/wihainan/p/6516858.html
(1)@Async:
在Spring中,基于@Async标注的方法,称之为异步方法;这些方法将在执行的时候,将会在独立的线程中被执行,即指将被注解的方法放到一个新创建线程,调用者无需等待它的完成,即可继续其他的操作。
使用方式:
在SpringBoot启动类上或Spring框架的Controller类加上@EnableAsync
@EnableAsync
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
// TODO Auto-generated method stub
SpringApplication.run(SpringbootApplication.class, args);
}
}
基于@Async无返回值调用
@Async //标注使用
2 public void asyncMethodWithVoidReturnType() {
3 System.out.println("Execute method asynchronously. "
4 + Thread.currentThread().getName());
5 }
基于@Async返回值的调用
@Async
2 public Future<String> asyncMethodWithReturnType() {
3 System.out.println("Execute method asynchronously - " + Thread.currentThread().getName());
4 try {
5 Thread.sleep(5000);
6 return new AsyncResult<String>("hello world !!!!");
7 } catch (InterruptedException e) {
8 //
9 }
10
11 return null;
12 }
注:@Async注解的方法要放在能被扫到的范围内