一、进程和多线程的概念以及线程的优点
打开Windo任务管理器可以看到很多正在运行着的exe程序,完全可以将运行在内存中的exe文件理解成进程,进程是受操作系统管理的基本运行单元。
线程可以理解成在进程中独立运行的子任务。比如,QQ.exe运行时就有很多的子任务在同时运行。
使用线程,可以最大限度地利用CPU的空闲时间来处理其他的任务,CPU在人物之间不停地切换,由于切换速度非常快,所以人们感觉这些任务似乎在同时运行。也就是说看,可以在同一时间内运行更多不同种类的任务,可以大幅增加CPU的利用率。
二、使用多线程
一个进程正在运行时至少会有一个线程在运行。
1 package test; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 System.out.println(Thread.currentThread().getName()); 7 } 8 9 }
输出为:main。这里输出的main其实就是一个名称叫做main的线程在执行main()方法中的代码。
1.继承Thread类
实现多线程编程的方式主要有两种:一种是继承Thread类,另一种是实现Runnable接口。从Thread类的结构来看,Thread类实现了Runnable接口,它们之间具有多台关系。为了支持多继承,完全可以实现Runnable接口的形式,一边实现一边继承。
1.1线程的调用的随机性
1 public class MyThread extends Thread { 2 @Override 3 public void run() { 4 super.run(); 5 System.out.println("MyThread"); 6 } 7 }
1 package test; 2 3 import com.mythread.www.MyThread; 4 5 public class Run { 6 7 public static void main(String[] args) { 8 MyThread mythread = new MyThread(); 9 mythread.start(); 10 System.out.println("运行结束"); 11 } 12 13 }
运行结束 MyThread
从输出结果可以看出,代码的运行结果与代码执行顺序或调用顺序是无关的,线程是一个子任务,CPU以不确定的方式,或者说是以随机的时间来调用线程中的run方法。
1.2 演示线程的随机性
1 package mythread; 2 3 public class MyThread extends Thread { 4 @Override 5 public void run() { 6 try { 7 for (int i = 0; i < 10; i++) { 8 int time = (int) (Math.random() * 1000); 9 Thread.sleep(time); 10 System.out.println("run=" + Thread.currentThread().getName()); 11 } 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 } 15 } 16 }
1 package test; 2 3 import mythread.MyThread; 4 5 public class Test { 6 public static void main(String[] args) { 7 try { 8 9 MyThread thread = new MyThread(); 10 thread.setName("myThread"); 11 thread.start(); 12 13 for (int i = 0; i < 10; i++) { 14 int time = (int) (Math.random() * 1000); 15 Thread.sleep(time); 16 System.out.println("main=" + Thread.currentThread().getName()); 17 } 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 } 22 }
main=main main=main run=myThread main=main run=myThread run=myThread main=main main=main run=myThread main=main main=main run=myThread main=main main=main run=myThread run=myThread run=myThread main=main run=myThread run=myThread
从输出的20个结果可以看出,Thread.java类中的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法。这个过程其实就是让系统安排一个时间来调用Thread中的run()方法,也就是使线程得到运行,启动线程,具有异步执行的效果。
1.3 同步执行
如果调用thread.run()方法就不是异步执行了,而是同步,那么此线程对象并不交给“线程规划器”来进行处理,而是由main主线程来调用run()方法,也就是必须等run()方法中的代码执行完后才可以执行后面的代码。
1 package extthread; 2 3 public class MyThread extends Thread { 4 5 private int i; 6 7 public MyThread(int i) { 8 super(); 9 this.i = i; 10 } 11 12 @Override 13 public void run() { 14 System.out.println(i); 15 } 16 17 }
1 package test; 2 3 import extthread.MyThread; 4 5 public class Test { 6 7 public static void main(String[] args) { 8 MyThread t11 = new MyThread(1); 9 MyThread t12 = new MyThread(2); 10 MyThread t13 = new MyThread(3); 11 MyThread t14 = new MyThread(4); 12 MyThread t15 = new MyThread(5); 13 MyThread t16 = new MyThread(6); 14 MyThread t17 = new MyThread(7); 15 MyThread t18 = new MyThread(8); 16 MyThread t19 = new MyThread(9); 17 MyThread t110 = new MyThread(10); 18 MyThread t111 = new MyThread(11); 19 MyThread t112 = new MyThread(12); 20 MyThread t113 = new MyThread(13); 21 22 t11.start(); 23 t12.start(); 24 t13.start(); 25 t14.start(); 26 t15.start(); 27 t16.start(); 28 t17.start(); 29 t18.start(); 30 t19.start(); 31 t110.start(); 32 t111.start(); 33 t112.start(); 34 t113.start(); 35 36 } 37 38 }
2 3 1 4 5 7 6 8 10 9 11 12 13
从输出结果可以看出,start()方法的顺序并不代表线程启动的顺序。
2.实现Runnable接口
如果想要创建的线程类已经有一个父类了,这时就不能再继承自Thread类了,因为Java不支持多继承,所以就需要实现Runnable接口。
1 package myrunnable; 2 3 public class MyRunnable implements Runnable { 4 @Override 5 public void run() { 6 System.out.println("myrunnable类中的run!"); 7 } 8 }
1 package test; 2 3 import myrunnable.MyRunnable; 4 5 public class Run { 6 7 public static void main(String[] args) { 8 Runnable runnable=new MyRunnable(); 9 Thread thread=new Thread(runnable); 10 thread.start(); 11 System.out.println("run中的main!"); 12 } 13 14 }
run中的main! myrunnable类中的run!
Thread.java类的8个构造函数中,有两个可以传递Runnable接口的对象,并且由于Thread类也实现了Runnable接口,所以也就意味着构造函数Thread(Runnable target)不光可以传入Runnable接口的对象, 还可以传入一个Thread类的对象,这样做完全可以将一个Thread对象中的run()方法交由其他的线程进行调用。
3.实例变量与线程安全
自定义线程类中的实例变量针对其他线程可以有共享与不共享之分。
3.1 不共享数据
3.2 共享数据
4.留意i--与System.out.println()的异常
三、currentThread()方法
四、isAlive()方法
五、sleep()方法
六、getId()方法
七、停止线程
1.停止不了的线程
2.判断线程是否是停止状态
3.能停止的线程--异常法
4.在沉睡中停止
5.能停止的线程--暴力停止
6.方法stop()与java.lang.ThreadDeath异常
7.释放锁的不良后果
8.使用return停止线程
八、暂停线程
1.suspend与resume方法的使用
2.suspend与resume方法的缺点--独占
3.suspend与resume方法的缺点--不同步
九、yield()方法
十、线程的优先级
1.线程优先级的继承特性
2.优先级具有规则性
3.优先级具有随机性
4.看谁运行得快
十一、守护线程
原文地址:https://www.cnblogs.com/BigJunOba/p/8979164.html