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 {@Overridepublic 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.实现Callblecallable比Runnable的功能更强大:1. **call()可以有返回值(**futureTask.get()**)**1. **方法可以抛出异常**1. **支持泛型的返回值**1. 需要借助**FutureTask**类,比如获取返回结果```javapackage 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@Overridepublic 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());//适合runnableservice1.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线程```javapackage 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{@Overridepublic 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{@Overridepublic 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、return1. 出现了未处理的异常1. 执行了wait()方法
:::
以下操作不会释放锁
1. 调用sleep()、yield()方法,只会暂停,不会释放锁1. 其他线程调用该线程的suspend()方法,将该线程挂起(不推荐使用)
