进程 比较好理解,打开Windows 的任务管理器进程页里的一个个 exe
就可以理解为一个进程。
线程可以理解是在进程中独立运行的子任务 ,具体见百度百科 https://baike.baidu.com/item/%E7%BA%BF%E7%A8%8B
创建一个线程主要有两种方式
Note: Thread 类其实是实现了 Runnable 接口的。使用继承 Thread 的方式创建线程时,最大的局限就是不支持多继承,因为 Java 是单根继承,为了支持多继承,完全可以实现 Runnable 接口的方法。两种方式在工作时的性质是一样的,没有本质的区别。
public class MyThreadWithRunnableimplements Runnable{ @Override public void run(){ System.out.println("--->"+this.getClass().getSimpleName()); } }
public class MyThreadWithThreadextends Thread{ public MyThreadWithThread(){ super(); setName(this.getClass().getSimpleName()); } @Override public void run(){ System.out.println("-->"+this.getName()); } }
public class Test{ public static void main(String[] args){ MyThreadWithThread threadWithThread = new MyThreadWithThread(); MyThreadWithRunnable threadWithRunnable = new MyThreadWithRunnable(); Thread thread = new Thread(threadWithRunnable); System.out.println("---begin"); thread.start(); threadWithThread.start(); System.out.println("---stop"); } }
Note: 如果多次调用 start() 方法 会出现异常 Exception in thread "main" java.lang.IllegalThreadStateException
Console
---begin ---stop -->MyThreadWithThread --->MyThreadWithRunnable
通过结果可以看到 代码的结果和代码的顺序是不一样的。
使用多线程时,代码的运行结果与代码执行顺序或者调用顺序是无关的。
线程是一个子任务,CPU 以不确定的方式,或者说以随机的时间来调用线程中的 run 方法。
线程共享数据的情况就是多个线程访问同一个变量。
多个线程在访问同一个变量的时候会出现非线程安全问题。
非线程安全主要是指多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程。
可以通过给代码上锁的方式解决这个问题。在方法上加上 synchronized 关键字。
当一个线程调用一个方法前会先判断这个方法有没有上锁,如果上锁了说明有其他线程正在调用此方法。必须等其他线程对run()方法调用结束后才能执行run()。在等待的同时线程会不断尝试去拿这个锁,而且是多个线程同时去拿,谁拿到谁 执行。这样就实现了排队调用run()方法。
synchronized 可以在任意对象及方法上枷锁,而加锁的这段代码成为“互斥区”或者“临界区”。
停止线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前的操作。
停止一个线程可以使用 Thread.stop()方法,但最好不用它,虽然它可以停止一个正在运行的线程,但是这个方法是不安全的,而且已经被废弃。
大多数停止一个线程的操作使用 Thread.interrupt()方法,尽管方法的名称是“停止,终止”的意思,当这个方法不会终止一个正在运行的线程,还需要加入一个判断才能完成线程的停止。
Java中有三种方法可以停止正在运行的线程
先看一下 Thread.interrupted()
/** * Tests whether the current thread has been interrupted. The * <i>interrupted status</i> of the thread is cleared by this method. In * other words, if this method were to be called twice in succession, the * second call would return false (unless the current thread were * interrupted again, after the first call had cleared its interrupted * status and before the second call had examined it). * * <p>A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * * @return <code>true</code> if the current thread has been interrupted; * <code>false</code> otherwise. * @see #isInterrupted() * @revised 6.0 */ public static boolean interrupted(){ return currentThread().isInterrupted(true); }
测试当前线程是否已经中断。 线程的 中断状态 通过此方法清除。换句话说就是,这个方法入如果连续两次调用,第二次将会返回 false(在第一次调用已清除了其中断状态后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)
线程中断被忽略是因为没有在存活的时候中断,这个方法将会返回 false
如果当前线程已经被中断了 将会返回 true
再看一下 isInterrupted
/** * Tests whether this thread has been interrupted. The <i>interrupted * status</i> of the thread is unaffected by this method. * * <p>A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * * @return <code>true</code> if this thread has been interrupted; * <code>false</code> otherwise. * @see #interrupted() * @revised 6.0 */ public boolean isInterrupted(){ return isInterrupted(false); }
测试该线程是否已经中断。线程的中断状态不受此方法影响。
可以看到两个方法的源码都是调用了 isInterrupted()
不同的是 interrupted()
是先调用 currentThread()
获取到当前代码运行所在的线程。然后让当前线程调用 isInterrupted()
。而 isInterrupted
这是直接调用,所以判断的是这个实例线程的状态。
再看一下 isInterrupted()
/** * Tests if some Thread has been interrupted. The interrupted state * is reset or not based on the value of ClearInterrupted that is * passed. */ private native boolean isInterrupted(boolean ClearInterrupted);
测试某些线程是否被中断。中断状态根据传递的ClearInterrupted的值决定重置或不重置。
Thread.interrupted()
传入了 false 所以会重置中断状态。
这里的所谓异常法停止就是对你想中断的线程调用 interrupt()
打上中断标识。在你执行操作的线程中一定要在某个地方检测 中断状态 如果中断状态为 true 了就停止操作。
public class MyThreadextends Thread{ public MyThread(String name){ super(name); } @Override public void run(){ try { System.out.println(Thread.currentThread().getName()+"---》begin"); for (int i=0;i<500000;i++){ System.out.println(Thread.currentThread().getName()+"-->"+i); if (this.isInterrupted()){ System.out.println("检测到当前线程实例中断标志("+this.getName()+")-->"+this.isInterrupted()); throw new InterruptedException(); } } System.out.println("for循环后执行-当前线程实例("+this.getName()+")中断标识->"+this.isInterrupted()+";当前代码运行线程("+Thread.currentThread().getName()+")中断标志--》"+Thread.interrupted()); System.out.println(Thread.currentThread().getName()+"----end--->"); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("异常法中断--》"+e.getMessage()); } } }
测试
public class Test{ public static void main(String[]args)throws InterruptedException { MyThread myThread =new MyThread("myThread"); myThread.start(); Thread.sleep(50); System.out.println("中断myThread"); myThread.interrupt(); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+" ====end--->"+myThread.isInterrupted()); }
Console
..... myThread-->7665 myThread-->7666 myThread-->7667 myThread-->7668 myThread-->7669 myThread-->7670 myThread-->7671 myThread-->7672 myThread-->7673 myThread-->7674 myThread-->7675 myThread-->7676 中断myThread myThread-->7677 检测到当前线程实例中断标志(myThread)-->true 异常法中断--》null java.lang.InterruptedException at com.skymxc.example.multithreading.stop.MyThread.run(MyThread.java:17) main ====end--->false Process finished with exit code 0
当然,这里是用的抛出异常的方法强行中断。也可使用 break 然后继续一个收尾工作。
关于 stop()方法 参考这篇文章 https://blog.csdn.net/jiangwei0910410003/article/details/19900007
暂停线程意味着还可以恢复线程的执行,在 Java 中使用 suspend()
暂停线程的执行,使用 ressume()
恢复线程的执行。
用一个例子看看这两个方法怎么用
public class CountThreadextends Thread{ public CountThread(){ super("countThread"); } private int i; public int getI(){ return i; } public void setI(int i){ this.i = i; } @Override public void run(){ SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS"); while (true){ i++; // System.out.println(dateFormat.format(new Date())+"--->"+i++); if (this.isInterrupted()){ break; } } System.out.println(dateFormat.format(new Date())+"---停止--》"+this.getName()); } }
测试
public class SuspendTest{ public static void main(String[]args)throws InterruptedException { SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS"); CountThread countThread = new CountThread(); countThread.start(); Thread.sleep(1000); //阶段 A 1000ms 后 countThread.suspend(); System.out.println( dateFormat.format(new Date())+"--A->"+countThread.getI()+";isInterrupted-->"+countThread.isInterrupted()); Thread.sleep(1000); //阶段B 1000ms 后 System.out.println( dateFormat.format(new Date())+"--B->"+countThread.getI()+";isInterrupted-->"+countThread.isInterrupted()); countThread.resume(); Thread.sleep(1000); //阶段C countThread.suspend(); System.out.println( dateFormat.format(new Date())+"--C->"+countThread.getI()+";isInterrupted-->"+countThread.isInterrupted()); Thread.sleep(1000); countThread.resume(); Thread.sleep(1000); countThread.interrupt(); //阶段D Thread.sleep(1000); System.out.println( dateFormat.format(new Date())+"--D->"+countThread.getI()+";isInterrupted-->"+countThread.isInterrupted()); } }
Console
17:48:11.934--A->516318612;isInterrupted-->false 17:48:12.935--B->516318612;isInterrupted-->false 17:48:13.935--C->1043644966;isInterrupted-->false 17:48:15.935---停止--》countThread 17:48:16.935--D->1565065219;isInterrupted-->false Process finished with exit code 0
确实达到了暂停和恢复的目的。
如果你在上面的 CountThread 中打印 i 你就会发现一个问题 在main 线程的 打印都没出来,而且程序已知在运行,没有结束,也没有log。
先看一下 println() 的实现
/** * Prints a String and then terminate the line. This method behaves as * though it invokes <code>{@link #print(String)}</code> and then * <code>{@link #println()}</code>. * * @param x The <code>String</code> to be printed. */ public void println(String x){ synchronized (this) { print(x); newLine(); } }
可以看到方法里 使用 synchronized 锁住了当前对象。
在例子中我们让 countThread 暂停,虽然它确实暂停了,但是没有释放锁,且一直在占着,这样的结果就是我们在main 线程的 打印一直在等锁,且一直等不到。
虽然 suspend()方法 已经被废弃,但是了解它为什么被废弃还是很有意义的。
因为它的独占 所以无法使用 synchronized ,也就无法保证数据同步
优先级较高的线程得到的CPU资源较多,也就是CPU优先执行级别较高的线程对象中的任务。
使用 setPriority()
方法设置线程优先级
public final void setPriority(int newPriority){ ThreadGroup g; checkAccess(); if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if((g = getThreadGroup()) != null) { if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); } }
优先级 分为 1 ~ 10 这10个等级。如果不在范围内就会抛出异常。
JDK预设了三个优先级的值:
/** * The minimum priority that a thread can have. */ public final static int MIN_PRIORITY = 1; /** * The default priority that is assigned to a thread. */ public final static int NORM_PRIORITY = 5; /** * The maximum priority that a thread can have. */ public final static int MAX_PRIORITY = 10;
end