本篇文章主要讲一些和线程,进程,的基础常识问题
1、进程和线程的基本概念
- 进程:一个程序启动(或是一个应用启动)就是一个程序,它是资源分配的基本单元,相当于是一个装很多线程的容器
- 线程:线程是CPU调度的基本单位,每个线程都会执行进程的一些代码片段
- 串行,并行,并发
- 串行:多个线程排队,一个一个的上
- 并行:多个线程一起上,这里必须是多个CPU或是大CPU多个核的情况下,才能实现并行
- 并发:区别于高并发(1w个请求同时访问这个接口)里的定义,这里的并发是指CPU在极短的时间内对不同的线程进行切换,来达到多个线程同时运行这样的效果。单核但CPU也可以并发但是不能并行。
- 同步,异步,阻塞,非阻塞
- 同步,异步:在调用一个功能时,看它是否会主动给调用者反馈信息,会主动反馈的就是异步,反之就是同步
- 阻塞,非阻塞:在调用一个功能时,此时调用者还能不能同时去干其他事情,如果可以,那就是非阻塞,反正就是阻塞
2、创建线程的方式
从本质上说,创建线程的方式只有一种,那就是创建一个线程对象,然后调用start方法。
- 从大的方面来说,创建线程的方法有三种
- 继承Tread类,重新run方法
public class MiTest {public static void main(String[] args) {MyJob t1 = new MyJob();t1.start();for (int i = 0; i < 100; i++) {System.out.println("main:" + i);}}} class MyJob extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("MyJob:" + i);}} }
- 实现Runable接口,重写run方法
public class MiTest {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread t1 = new Thread(myRunnable);t1.start();for (int i = 0; i < 1000; i++) {System.out.println("main:" + i);}}} class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("MyRunnable:" + i);}} }
它对应的还有两种常见的实现方式
1. 匿名内部类
2. lambad方式// 匿名内部类方式 Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("匿名内部类:" + i);}} });
// lambad表达式的方式 Thread t2 = new Thread(() -> {for (int i = 0; i < 100; i++) {System.out.println("lambda:" + i);} });
- 实现Callable接口,重写call方法
Callable它的call方法可以带结果返回,而且他是一种非阻塞的同步方法
public class MiTest {public static void main(String[] args) throws ExecutionException, InterruptedException {//1. 创建MyCallableMyCallable myCallable = new MyCallable();//2. 创建FutureTask,传入CallableFutureTask futureTask = new FutureTask(myCallable);//3. 创建Thread线程Thread t1 = new Thread(futureTask);//4. 启动线程t1.start();//5. 做一些操作//6. 要结果Object count = futureTask.get();System.out.println("总和为:" + count);} }class MyCallable implements Callable{@Overridepublic Object call() throws Exception {int count = 0;for (int i = 0; i < 100; i++) {count += i;}return count;} }
3、线程的使用方式
3.1、线程的状态
- new:Thread对象被创建出来,但是还没有执行start方法
- runnnable:对象调用了start方法,正常运行对应的代码
- blocked:被阻塞,如在多线程加锁的情况下,对应的线程没有拿到锁,就会处于blocked状态
- waiting:调用wait方法,这种需要手动唤醒
- time_waiting:调用sleep或是join方法,到了时间或是特定的条件达成就会唤醒,继续跑
- terminated:run方法执行完之后,线程对应的状态
3.2、现成常用的方法
- 获取当前线程:Thread.currentThread()
public static void main(String[] args) throws ExecutionException, InterruptedException {// 获取当前线程的方法Thread main = Thread.currentThread();System.out.println(main);// "Thread[" + getName() + "," + getPriority() + "," + group.getName() + "]";// Thread[main,5,main]
}
- 线程的名称:thread.getName()
public static void main(String[] args) throws ExecutionException, InterruptedException {Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getName());});t1.setName("模块-功能-计数器");t1.start();
}
- 线程的优先级:thread.setPriority(1)
其实它就是CPU调度线程的优先级,Java中给线程设置了1~10的优先级别
public static void main(String[] args) throws ExecutionException, InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {System.out.println("t1:" + i);}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {System.out.println("t2:" + i);}});t1.setPriority(1);t2.setPriority(10);t2.start();t1.start();
}
- 线程的让步:Thread.yield()
通过Thread的静态方法yield调用,让当前线程从运行状态转变为就绪状态
public static void main(String[] args) throws ExecutionException, InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {if(i == 50){Thread.yield();}System.out.println("t1:" + i);}});Thread t2 = new Thread(() -> {for (int i = 0; i < 100; i++) {System.out.println("t2:" + i);}});t2.start();t1.start();
}
- 线程的休眠:Thread.sleep()
让线程从运行状态变为等待状态,sleep方法会抛出一个InterruptedException
public static void main(String[] args) throws InterruptedException {System.out.println(System.currentTimeMillis());Thread.sleep(1000);System.out.println(System.currentTimeMillis());
}
- 线程的强占:thread.join()
假如在t1中运行t2.jooin(),t1会进入等待状态,一直等到t2运行完毕之后,t1才会进入就绪状态等待CPU运行,如果是t2.join(2000),t1会等t2运行2s之后再开始运行,如果在这2s之内,t2先运行完了,t1也会提前开始运行
public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 10; i++) {System.out.println("t1:" + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t1.start();for (int i = 0; i < 10; i++) {System.out.println("main:" + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if (i == 1){try {t1.join(2000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
- 守护线程
- 现成的等待和唤醒
- wait, notify() , notifyAll()
public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {sync();},"t1");Thread t2 = new Thread(() -> {sync();},"t2");t1.start();t2.start();Thread.sleep(12000);synchronized (MiTest.class) {MiTest.class.notifyAll();} }public static synchronized void sync() {try {for (int i = 0; i < 10; i++) {if(i == 5) {MiTest.class.wait();}Thread.sleep(1000);System.out.println(Thread.currentThread().getName());}} catch (InterruptedException e) {e.printStackTrace();} }
3.3、线程的结束方式
让线程停止的方法很多,比如:调用stop(),通过共享变量来破坏死循环来让线程运行结束停止掉,通过interrupt()
其中通过interrupt(),通过打断线程的方式结束时最常见的,也是比较优雅的一种方式
public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while(true){// 获取任务// 拿到任务,执行任务// 没有任务了,让线程休眠try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();System.out.println("基于打断形式结束当前线程");return;}}});t1.start();Thread.sleep(500);//把t1停止掉,此时会进行t1.interrupt();
}
3.3、wait和sleep的区别
- sleep属于Thread类的Static方法,wait是Object类的方法
- sleep属于Timed_waiting,它根据设置自动唤醒,wait属于Waiting,需要手动唤醒
- sleep方法在持有锁时,是不会释放锁资源的,wait在执行之后,会释放锁资源
- sleep方法在有无锁的情况下都可以执行,但是wait方法只有在有锁的时候运行
- wait方法会将持有锁的线程从owner扔到waitSet中,这个操作是在修改ObjectMonitor对象,如果没有持有synchronized锁的话,是无法操作ObjectMonitor独享的