一,进程,线程的概念:来自百度百科
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。
线程(英语:thread)是操作系统能够进行运算调度的最小单位。
二,Java中线程创建线程的几种方法
1,extends Thread :这种的局限性是不支持多继承,这也是受Java语言本身限制。其实Tread类是实现了Runnable<>
public
class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
2,implements Runnable<> 。
3,implements Callable<> 。相比于前面创建的线程中,前者是实现 run() 这个是没有返回值的,而call()方法是有返回值的,而且可以声明异常,功能更加强大,这种实现是Java 为高并发提供的一种Future模式的一种封装。
三,run() ,start()方法的区别:
若调用直接调用run(),就像调用普方法一样。如果有多个任务,那么第二个任务必须等到第一个任务执行完成后才会执行第二个,这时整个系统调用就是同步的。
调用start()方法 ,系统会吧线程交给"线程规划器" ,让系统自行调度线程,这时线程的调用就会有随机性, 如果第一个任务在等待,那么第二个线程也会被调用,这时线程的调度就是异步 的,
四,线程安全,当多个线程访问同一个资源,如果不做同步处理,那么使的某个线程读取的资源不是我们所期望的值。为什么会出现这种情况呢
1,先了解一下Java的内存模型 JMM
这是线程,主内存,工作内存的关系:
1,Java内存模型规定,所有变量会存贮在主内存中, 每个线程都会有自己的工作内存,私有,不共享,工作内存保存的变量都是主内存的变量副本拷贝,线程对变量的读取只能在工作内存,线程无法直接操作主内存的数据,重点 : 线程之间变量值的传递需要通过主内存来完成,怎么理解,举个例子:假如一个线程要读取一个变量,首先主内存会读取这个变量值到工作内存中(这里涉及read操作),然后工作内存吧这个读取的值载入工作内存对应这个变量副本的的变量上(这是load操作,之前这个变量必须是先进行read操作的),这时执行引擎(可以简单的认为是Java vm stack上当前栈帧上的操作数栈,对数据的读写就是出入栈操作)就会把这个值压入当前操作数栈的栈顶
ps:具体分析可以看看《深入理解Java虚拟机》
那么当多个线程访问同一个变量时,因为工作内存和主内存之间存在copy ,那就需要时间,从而导致某个线程读取的值并不是主内存中最新的值,或者是旧的值。这时就是导致非线程安全的问题。
2,与Java编程中的有所不同,这里的变量主要包括 实例字段,静态字段,构成数组组成的元素,不包括局部变量,他是线程私有的,不会被共享,也就不会存在竞争。
解决办法就是 加锁。
五,加锁,synchronized 的使用 被加锁的代码块被称为互斥区,临界区,每个线程会争抢去获得这个互斥区的锁,强不到锁的线程就会等待。直到拿到这把锁
1,在方法上加锁
public class Person{
public String name;
public synchronized void sayHello(String name){
Sysyem.out.println(name+"say hello");
}
}
2,使用同步代码块
public class Person{
public String name;
public void sayHello(String name){
synchronied(this){
Sysyem.out.println(name+"say hello");
}
}
}
效果一样,有时第二种会更好。这里只是简单介绍。
六,如何正确停止一个线程。
1,使用interrupt(),而不使用stop()该方法已经废除。但前者调用后并不会立即停止线程
这个方法是判断当前线程是否已经中断。但会清楚中断标记
这个方法是判断线程是否已经中断,不清楚中断标记
这是一个本地方法 参数 ClearInterrupted:是否清除中断标志
中断方法
能力有限o'o,知道前两个方法的区别就行
2,异常法中断线程
简单的例子
public class MyThread implements Runnable{
@Override
public void run(){
try{
if(this.interruptor()){
throws new Execption("中断线程");
}
}catch(Exception e){
e.printStackTrace();
}
}
}
3,在沉睡中中断线程
public class SleepInterruptDemo extends Thread {
public void run() {
super.run();
try {
for (int i = 0; i < 500000; i++) {
System.out.println("i=" + (i + 1));
}
System.out.println("run begin");
Thread.sleep(2000);
System.out.println("run end");
} catch (Exception e) {
System.out.println("进入run方法中的catch了!!");
e.printStackTrace();
}
}
public static void main(String[] args) {
SleepInterruptDemo thread = new SleepInterruptDemo();
thread.start();
thread.interrupt();
System.out.println("main end!");
}
}
小细节:若果在中断前再次沉睡,这时调用中断方法 会清除中断标记,即再thread.interrupt()前面加一个Thread.sleep(100)。
建议使用异常法
七,几个常用的方法:
1,yelid() 使当前线程放弃cpu的资源,将它让给其他线程去执行。
2,Thread.currentThread.getId() 获取当前线程的id标识
3,isAlive() 判断当前方法是否存活
4,setPriority(int num) 设置线程的优先级 Java规定0~10 超出报错,等级越高越有可能抢到cpu的资源
1)具有继承性 a 线程启动b线程,那么b线程拥有和a一样的优先级。
2)规则性,优先级高的线程大部分会先于低的执行玩,
3)随机性,优先级高的线程不一定都先执行完