为什么要学习多线程与高并发?
因为面试。或者初级程序员迈向中高级的一道坎
线程
1.1、线程的基本概念
什么是线程?
线程是相对于进程而言。进程里最小的执行单元
什么是进程?
好比一个程序。qq。在静止状态下。你没去点他。称为程序。当你点击启动qq。这就是启动了一个进程
package com.example.demo;
import java.util.concurrent.TimeUnit;
public class T01_whatisThread {
private static class T1 extends Thread{
@Override
public void run(){
for (int i=0;i<10;i++){
try{
TimeUnit.MICROSECONDS.sleep(i);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("T1");
}
}
}
public static void main(String[] args) {
new T1().start();
for (int i=0;i<10;i++) {
try{
TimeUnit.MICROSECONDS.sleep(i);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("main");
}
}
}
执行以后会看到t1和main交替输出
1.2、线程创建的几种方式
package com.example.demo;
import java.util.concurrent.*;
public class T02_howtoCreateThread {
// 1、继承Thread类
static class MyThread extends Thread{
@Override
public void run(){
System.out.println("hello myThread");
}
}
// 2、实现runnable接口
static class MyRun implements Runnable{
@Override
public void run() {
System.out.println("hello myRun");
}
}
// 3、实现Callable接口
static class MyCall implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("hello Mycall");
return "success";
}
}
public static void main(String[] args) {
// 启动线程的5种方式
// 1
new MyThread().start();
// 2
new Thread(new MyRun()).start();
// 3
new Thread(() -> {
System.out.println("hello lambda");
}).start();
// 4
Thread t = new Thread(new FutureTask<String>(new MyCall()));
t.start();
// 5
ExecutorService service = Executors.newCachedThreadPool();
service.execute(() ->{
System.out.println("hello threadPoll");
});
service.shutdown();
}
}
####面试题:启动线程的三种方法?
1、new Thread继承类.start()
2、new Thread(new Runnable实现类).start()
3、线程池创建
3.1、先用Executors的方法创建ExecutorService对象
3.1.1、Executors创建4种线程池为哪四种?
1、newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
2、newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3、newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
4、newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
3.2、调用execute()方法执行线程池
3.3、执行完毕后调用shutdown方法关闭线程池
####面试题:创建线程有几种方法?
1、继承Thread类
2、实现Runnable接口
3、实现Callable接口
####面试题:为什么线程执行调用的是start方法而不是run方法?
直接调用线程的run方法。没有实现多线程。而是当前线程立即执行完run方法后再执行下一句代码。而start方法真正使线程进入就绪状态。等待cpu分配时间片。而不是立即执行。
package com.example.demo;
public class T03_sleep_yield_join {
static class MyThread extends Thread{
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+"============");
}
}
public static void main(String[] args) {
MyThread m = new MyThread();
// 定义两个线程名
Thread thread1 = new Thread(m,"thread1");
Thread thread2 = new Thread(m,"thread2");
//测试直接用run方法和start方法区别
thread1.run();
thread2.start();
}
}
所以直接调run方法会出错
1.3、线程的几种方法
package com.example.demo;
public class T03_sleep_yield_join {
public static void main(String[] args) {
//testSleep();
//testYield();
testJoin();
}
/**
* sleep,睡眠。当前线程暂停一段时间让给别的线程去运行。当前线程由睡眠时间决定。等
* 睡眠时间到规定时间自动复活
*/
static void testSleep(){
new Thread(() -> {
for (int i = 0; i<100 ;i++){
System.out.println("A"+i);
try{
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}).start();
}
/**
* yield 当前线程正在执行时候停下来进入等待队列。回到等待队列的他依旧可能会被拿出来
* 继续执行。当然更大程度和可能是原来等待的线程。也就是我让出cpu,抢不抢得到是你们的事
*/
static void testYield(){
new Thread(() -> {
for (int i = 0; i<100 ;i++){
System.out.println("A"+i);
if (i%10 == 0) Thread.yield();
}
}).start();
new Thread(() -> {
for (int i = 0; i<100 ;i++){
System.out.println("B"+i);
if (i%10 == 0) Thread.yield();
}
}).start();
}
/**
* join 自己当前线程和调用join的线程执行时。当前线程等待。等调用线程执行完毕。当前线程再执行。
* t1 t2两个线程。在t1执行过程中调用t2.join()则跑到t2执行完毕后再执行t1.一般不自己join自己
* 无意义
*/
static void testJoin(){
Thread t1 = new Thread(() ->{
for (int i = 0; i< 100; i++){
System.out.println("A"+i);
try {
Thread.sleep(0);
}catch (InterruptedException e){
e.printStackTrace();
}
}
});
Thread t2 = new Thread(() -> {
try {
t1.join();
}catch (InterruptedException e){
e.printStackTrace();
}
for (int i = 0 ; i< 100; i++){
System.out.println("B"+i);
try {
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
t2.start();
}
}
sleep
yield
join
sleep当前线程睡眠。让给别的线程执行。睡眠时间到当前线程复活
yield线程礼让。让出cpu。进入等待区。但同时也可能被继续执行。其他线程能不能抢到资源都不一定
join 在t1线程里调用t2.join方法。直接执行t2线程。直到t2执行完毕后再回来执行t1
1.4、线程的六种状态
1、 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
2、 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
3、 阻塞(BLOCKED):表示线程阻塞于锁。
4、 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
5、 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
6、 终止(TERMINATED):表示该线程已经执行完毕
####线程的几种状态都由jvm管理?还是由操作系统管理?
全部都由jvm管理,jvm是操作系统一个普通程序。两者不分家
####线程什么状态会被挂起。挂起是否也是一种状态?
运行中的时候,cpu执行多个线程。一会执行这个线程一下。一会执行那个一下。当线程被cpu挂起的时候。被动的。所以挂起不是线程自己的状态。
package com.example.demo;
public class T04_ThreadState {
static class MyThread extends Thread{
@Override
public void run(){
System.out.println(this.getState());
for(int i = 0; i<10; i++){
try{
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(i);
}
}
public static void main(String[] args) {
Thread t = new MyThread();
System.out.println(t.getState()); // new状态
t.start(); // Runnable状态
try{
t.join();
}catch (InterruptedException e){
e.printStackTrace();
}
// join之后是Timenated状态
System.out.println(t.getState());
}
}
}