1. 进程和线程
进程是一个应用程序,每个进程都有自己的独立内存空间,进程不依赖线程而独立存在,一个进程可以启动多个线程。
线程是进程的执行单元,一个进程可以运行多个线程。
在Dos中输入java HelloWorld后,程序的执行过程:
会先启动Jvm,Jvm就是一个进程,Jvm在启动一个主线程执行main方法,在启动一个垃圾回收线程负责看护,回收垃圾,至少有两个线程并发运行,一个垃圾回收线程,一个执行main方法的线程。
在Java中,线程A和线程B,堆内存和方法区内存共享,但是栈内存独立,一个线程一个栈;所以main方法结束了,只代表主线程结束了,其他线程可能还在执行。
2. 线程的生命周期
- 新建状态:
线程创建后,就进入新建状态。例如:Thread thread = new Thread();。
- 就绪状态:
当线程对象调用了start()方法后,线程就进入了就绪状态,等待CPU调度。
- 运行状态:
线程获取CPU资源,就可以运行,就处于运行状态。
- 阻塞状态:
线程因为其他原因失去CPU资源暂时停止运行。直到线程进入就绪状态,才可以重新进入运行状态。阻塞状态有三种:
(1)等待阻塞:线程执行wait()方法,就进入等待阻塞状态。
(2)同步阻塞:线程获取synchronized同步锁失败(同步锁被其他线程占用中)
(2)其他阻塞:通过调用sleep()或join()发出I/O请求时,进入阻塞状态。
- 死亡状态:
线程执行完成或被其他任务强制终止
3. 实现线程的两种方式
通过继承Thread创建线程
public class ThreadTest01{ public static void main(String[] args){ MyThread thread = new MyThread(); thread.start(); for(int i=0; i<1000; i++){ System.out.println("主线程----" + i); } } } class MyThread extends Thread{ public void run(){ for(int i=0; i<1000; i++){ System.out.println("分支线程----" + i); } } }
结果说明:
如果单纯调用MyThread.run(),那么只是在main()方法上压栈,没有创建分支线程;
先调用start()方法开辟一个分支栈空间,然后会自动调用run(),在分支栈中和主栈中并发同时输出。
实现Runnable接口创建线程
public class ThreadTest02{ public static void main(String[] args){ Thread thread = new Thread(new MyRunnable()); //创建一个可运行对象,创建接口,把可运行对象封装成线程对象 thread.start(); for(int i=0; i<1000; i++){ System.out.println("主线程----" + i); } } } class MyRunnable implements Runnable{ public void run(){ for(int i = 0; i<1000; i++){ System.out.println("分支线程----" + i); } } }
4. 线程方法
(1)getName() 获取线程名,默认线程名Thread-0;
(2)setName() 设置线程名;
(3)Thread.currentThread() 获取当前线程名,返回Thread,在哪个线程中就获取哪个线程名
public class ThreadTest03{ public static void main(String[] args){ Thread currentThread = Thread.currentThread(); System.out.println("当前线程:" + currentThread.getName()); MyThread mt = new MyThread(); mt.setName("tt"); System.out.println(mt.getName()); mt.start(); } } class MyThread extends Thread{ public void run(){ for(int i = 0; i<5; i++){ Thread currentThread = Thread.currentThread(); System.out.println(currentThread.getName() + "分支线程:" + i); } } }
Thread.sleep(long mills) 让当前线程等待mills(毫秒)
public class ThreadTest04{ public static void main(String[] args){ for(int i=0; i<5; i++){ System.out.println(Thread.currentThread().getName() + "----" + i); try{ Thread.sleep(1000); }catch(Exception e){ e.printStackTrace(); } } } }
interrupt() 线程中断
public class ThreadTest05{ public static void main(String[] args){ Thread t = new Thread(new MyRunnable()); t.setName("tt"); t.start(); try{ Thread.sleep(5000); }catch(InterruptedException e){ e.printStackTrace(); } t.interrupt(); } } class MyRunnable implements Runnable{ public void run(){ System.out.println(Thread.currentThread().getName() + "---start"); try{ Thread.sleep(1000*60*60*24*365); //父类中没有抛出异常,子类中只能try...catch...捕获异常 }catch(InterruptedException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "---end"); } }
当线程被wait,sleep,join等阻塞时,调用该线程的interrupt()方法,那么该线程将抛出一个InterruptedException异常(该线程必须提前处理此异常),从而提早的终结被阻塞状态。如果线程没有被阻塞,这是调用interrupt()将不起作用。线程被唤醒的机制也是异常处理机制,当有异常发生时,就可以唤醒线程。
stop() 终止线程
public class ThreadTest06{ public static void main(String[] args){ MyRunnable mr = new MyRunnable(); Thread t = new Thread(mr); t.setName("tt"); t.start(); try{ Thread.sleep(5000); }catch(InterruptedException e){ e.printStackTrace(); } //t.stop(); mr.flag = false; } } class MyRunnable implements Runnable{ boolean flag = true; public void run(){ for(int i=0; i<10; i++){ if(flag){ System.out.println(Thread.currentThread().getName() + "---" + i); try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } }else{ return; } } } }
stop() 终止线程因为安全性问题,现在已经不推荐使用。
5. 线程调度
getPriority() 获取线程优先级
setPriority() 设置线程优先级
public class ThreadTest07{ public static void main(String[] args){ /* System.out.println(Thread.currentThread().getName() + "优先级:" + Thread.currentThread().getPriority()); ThreadTest t = new ThreadTest(); t.setName("tt"); t.start(); System.out.println("线程的最高优先级:"+Thread.MAX_PRIORITY); //10 System.out.println("线程的最低优先级:"+Thread.MIN_PRIORITY); //1 System.out.println("线程的正常优先级:"+Thread.NORM_PRIORITY); //5 */ ThreadTest tt = new ThreadTest(); tt.setName("tt"); tt.setPriority(10); tt.start(); for(int i=0; i<1000; i++){ System.out.println(Thread.currentThread().getName() + "--->" + i); } } } class ThreadTest extends Thread{ public void run(){ //System.out.println(Thread.currentThread().getName() + "优先级:" + Thread.currentThread().getPriority()); for(int i=0; i<1000; i++){ System.out.println(Thread.currentThread().getName() + "--->" + i); } } }
线程优先级的范围是1-10,默认优先级是5。
高优先级线程被分配CPU的概率高于低优先级线程,但是无论级别相同还是不同,线程调度都不对绝对按照优先级执行,根据时间片轮询调度,并发执行。
yield() 线程让位,让线程暂停回到就绪状态
public class ThreadTest08{ public static void main(String[] args){ Thread th = new Thread(new yieldTest()); th.setName("tt"); th.start(); for(int i=0; i<10000; i++){ System.out.println(Thread.currentThread().getName() + "--->" + i); } } } class yieldTest implements Runnable{ public void run(){ for(int i=0; i<10000; i++){ if(i % 100 == 0){ Thread.yield(); //每过100,让位给主线程 } System.out.println(Thread.currentThread().getName() + "--->" + i); } } }
yield()作用时让步,让当前线程由运行状态进入到就绪状态,从而让其他具有相同优先级的线程获取执行权;但是并不能保证当前线程调用yield后,其他线程线程就一定能获取执行权,也有可能当前线程又继续进入运行状态。
join() 线程合并,让主线程等待子线程执行完后再继续执行主线程。
public class ThreadTest09{ public static void main(String[] args){ System.out.println("main begin"); Thread th = new Thread(new joinTest()); th.setName("tt"); th.start(); try{ th.join(); //加入到主线程中,执行完后,再执行其他线程 }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("main over"); } } class joinTest implements Runnable{ public void run(){ for(int i=0; i<100; i++){ System.out.println(Thread.currentThread().getName() + "--->" + i); } } }
6. 守护线程
setDaemon() 守护线程,一般是死循环,只要主线程结束,守护线程就自动结束
public class ThreadTest10{ public static void main(String[] args){ Thread th = new Thread(new MyThread()); th.setName("备份数据线程"); th.setDaemon(true); //备份数据线程设置为用户线程的守护线程,用户线程结束,备份数据线程结束 th.start(); //用户线程 for(int i=0; i<10; i++){ System.out.println(Thread.currentThread().getName() + "--->" + i); try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } } } } class MyThread implements Runnable{ int i=0; public void run(){ while(true){ System.out.println(Thread.currentThread().getName() + "--->" + (++i)); try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } } } }
7. 定时器
定时器,间隔一定时间,执行指定程序,java.util.Timer定时器
public class ThreadTest11{ public static void main(String[] args) throws ParseException{ Timer timer = new Timer(); //创建定时器 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); Date date = sdf.parse("2021-03-21 21:18:20"); timer.schedule(new LogTimer(),date,1000*10); //每隔10s完成次数据备份 } } class LogTimer extends TimerTask{ public void run(){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String nowtime = sdf.format(new Date()); System.out.println(nowtime + "完成了一次数据备份"); } }
8. 实现线程的第三种方式
实现callable接口,可以获取线程的返回值;
public class ThreadTest12{ public static void main(String[] args) throws ExecutionException, InterruptedException{ FutureTask futuretask = new FutureTask(new Callable(){ public Object call() throws Exception{ System.out.println("method begin"); Thread.sleep(1000*5); System.out.println("method over"); int a = 100; int b =200; return a + b; } }); Thread th = new Thread(futuretask); th.setName("tt"); th.start(); Object obj = futuretask.get(); //get()方法执行会使主线程进入阻塞状态,主线程必须等待子线程执行完成返回结果 System.out.println("获取返回值: "+ obj); } }
这种方式创建线程:
优点:可以获取线程的返回值;
缺点:必须等待子线程执行完得到返回结果后,才能执行主线程。