0.前言
- 并发:同一时刻,多个任务交替执行,单核多任务就是并发
-
线程的7大状态
1.线程使用
1.继承Thread
继承Thread类,重写run方法即可,在run方法中编写线程逻辑,创建对象后以start()启动线程,而start()中调用了start0(),start0()是本地方法,由jvm调用 private native void start0();
- 此方法创建的线程不能使用this作为锁,因为是new创建的对象,this是不同的
package review.thread;
/**
* Thread方式创建线程
* @Author rainbow
* @Date 2022/5/15 18:54
* @Version 1.0
**/
public class ThreadTest extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ThreadTest threadTest = new ThreadTest();
ThreadTest threadTest1 = new ThreadTest();
//start不是让线程立即执行,而是成为就绪状态,等待被cpu调用,不会阻塞后面的代码执行
threadTest.start();
threadTest1.start();
}
}
2.实现Runnable
- 当类已经继承了其他类却需要多线程时,可以用实现接口的方法,并通过将线程对象传给thread构造器创建thread对象,再调用start()(静态代理模式)
- 适合多个线程共享数据时使用,优先推荐此方式实现多线程
- 可以使用同一个对象创建多个线程 ```java package review.thread;
/**
- Runnable创建线程
- @Author rainbow
- @Date 2022/5/15 19:29
@Version 1.0 **/ public class RunnableTest implements Runnable{ @Override public void run() {
System.out.println("runnable创建的线程");
}
public static void main(String[] args) {
//创建线程对象,但是没有start方法,不能直接调用
RunnableTest runnableTest = new RunnableTest();
//作为构造器参数创建thread对象,再调用start()
Thread thread = new Thread(runnableTest);
Thread thread1 = new Thread(runnableTest);
thread.start();
thread1.start();
} }
<a name="TzKBk"></a>
## 3.实现Callble
callable比Runnable的功能更强大:
1. **call()可以有返回值(**futureTask.get()**)**
1. **方法可以抛出异常**
1. **支持泛型的返回值**
1. 需要借助**FutureTask**类,比如获取返回结果
```java
package threadExample;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//使用callable创建多线程
/*
* callable比Runnable的功能更强大:
* call()可以有返回值
* 方法可以抛出异常
* 支持泛型的返回值
* 需要借助FutureTask类,比如获取返回结果
* */
//1.创建callable的实现类
class NumThread implements Callable{
//2.实现call方法,类似run
@Override
public Object call() throws Exception {
int sum=0;
for (int i = 0; i < 100; i++) {
if (i % 2 != 0) {
System.out.println(Thread.currentThread().getName() + ":\t" + i);
sum+=i;
}
}
return sum;
}
}
public class CallableTest {
public static void main(String[] args) {
//3.创建callable实现类对象
NumThread numThread = new NumThread();
//4.创建futureTask对象
FutureTask futureTask = new FutureTask(numThread);
//5.传给thread,并调用start()
new Thread(futureTask).start();
try {
//返回值即为FutureTask构造器参数Callable实现的call方法返回值
Object sum= futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
4.线程池
:::danger
提前创建好多个线程,放入线程池中,使用时直接获取,使用完 放回池中。可以避免频繁创建销毁、实现重复利用。
:::
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理(需要转实现类ThreadPoolExecutor)
- corePoolSize:核心池的大小
- maximumPoolSize:最大线程数
- keepAliveTime:线程没有任务时最多保持多长时间后会终止
```java package threadExample;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor;
class number implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + “:\t” + i); } } } }
class number1 implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 != 0) { System.out.println(Thread.currentThread().getName() + “:\t” + i); } } } }
//使用线程池的方法创建现程 public class ThreadTest { public static void main(String[] args) { //创建10个线程的线程池 ExecutorService service = Executors.newFixedThreadPool(10); //转类型是因为接口中没有配置属性,要设置就需要转类型 ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; //设置最大线程数 service1.setMaximumPoolSize(10);
//执行指定线程,提供一个实现runnable或callable接口的对象(submit())
service1.execute(new number());//适合runnable
service1.execute(new number1());//适合runnable
// service1.submit() 适合callable //关闭线程池 service1.shutdown(); }
}
<a name="NxXrK"></a>
# 2.线程常用方法
1. setName():设置线程名
1. getName():获得线程名
1. start():使线程就绪
1. setPriority():设置线程优先级
1. getPriority():获得线程优先级
1. **sleep()**:使线程休眠,但是**不会释放锁**
1. interrupt():中断线程
1. **yield()**:让出cpu给其他线程,但是时间不确定,所以**不一定成功(**可能让之后又被自己抢到),**不会释放锁**
1. **join()**:线程插队,一旦插队成功,则**先执行完插入线程的所有任务**
1. **setDaemon(true)**:将线程设置为**守护线程**(当所有**用户线程结束时**,自动结束,例如垃圾回收机制)
1. **以下方法必须在同步方法或代码块中由同步监视器(加锁的对象)调用**
1. **wait()**:使当前线程进入阻塞,并释放锁,等待被唤醒
1. notify():随机唤醒一个被wait的线程
1. **notifyall()**:唤醒所有被wait线程
```java
package review.thread;
/**
* 守护线程
* @Author rainbow
* @Date 2022/5/16 10:40
* @Version 1.0
**/
public class DaemonTest {
public static void main(String[] args) throws InterruptedException {
MyDaemon myDaemon = new MyDaemon();
Thread thread = new Thread(myDaemon);
//将子线程设置为守护进程,当所有的用户线程结束时,会自动结束
thread.setDaemon(true);
thread.start();
Thread1 thread1 = new Thread1();
thread1.start();
/**
* 让子线程执行结束才执行主线程,因为守护线程先start,且join是针对主线程的
*(写在主线程中的),所以守护线程会和子线程一起执行
* 而如果先启动子线程再启动守护线程,则顺序是子线程结束,守护线程和主线程才开始
*/
thread1.join();
for (int i = 0; i < 10; i++) {
System.out.println("主线程执行...");
Thread.sleep(500);
}
}
}
class Thread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("子线程执行...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyDaemon implements Runnable{
@Override
public void run() {
while (true){
System.out.println("守护线程执行中...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.线程同步
1.synchronized方式
- 保证数据操作的完整性,会导致程序效率降低
- 每个对象都对应一个可称为互斥锁的标记,保证在同一时刻只能有一个线程访问对象
- 关键字synchronized来与对象的互斥锁联系,当对象被synchronized修饰时,表示任一时刻只能被一个线程访问
- synchronized可以修饰方法或代码块,优先使用同步代码块
- 非静态同步方法的锁可以是this,也可以是其他对象(并没有其他含义,就是以一个对象作为锁,谁持有这个对象谁就可以操作代码块,类似于钥匙,而谁是钥匙并不重要,只需要始终是同一个把钥匙即可)
- 静态同步方法(static修饰的方法)的锁是当前类本身(类名.class)
- 静态方法锁默认当前类.class,非静态方法锁默认this
必须保证锁的对象是多个对象共享的(例如通过继承Thread类实现的线程就不能用this锁,因为他们各自是new的新对象)
2.Lock锁
jdk5.0新增的接口,能够显式加锁与释放锁,需要使用具体的实现类ReentrantLock
- 加锁: lock.lock(),解锁:lock.unlock()
- 与synchronized区别是:
- synchronized会自动释放锁
- lock需要手动的加锁与解锁
3.释放锁
:::danger 以下操作会释放锁
1. 当前线程的同步方法、同步代码块执行完毕
1. 在同步方法或代码块中遇到break、return
1. 出现了未处理的异常
1. 执行了wait()方法
:::
以下操作不会释放锁
1. 调用sleep()、yield()方法,只会暂停,不会释放锁
1. 其他线程调用该线程的suspend()方法,将该线程挂起(不推荐使用)