在上一篇 JAVA高并发——了解并行世界 中我们回顾了一些多线程的概念知识。这里先举个例子,来看现实生活中的多线程例子:一个家庭中有爸爸、妈妈、儿子,家中有电视、洗衣机、书桌等,如果妈妈领着儿子出去了,爸爸就可以想干什么干什么(单线程);如果三人都在家,妈妈可以洗衣服、爸爸在书桌上工作、儿子看动画片(三个线程使用不同的资源互不影响);如果儿子看动画片,爸爸想看NBA视频,就得等着儿子看完了(多线程公用资源,出现阻塞)……看见了吧,其实我们就是把程序编写的如何像现实生活一样,多个任务同时进行,有共享资源,有各自资源,但是大家能够有条不紊的进行着……
好,这篇我们来复习一下Java多线程的一些基础知识,来看下这张思维导图:
一,首先需要了解线程的状态有哪些,怎么扭转,其实也就是生命周期(现实生活做事也是可以这样抽象)。我先看下Java,Thread类中对于state的定义:
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
*
* - {@link Object#wait() Object.wait} with no timeout
* - {@link #join() Thread.join} with no timeout
* - {@link LockSupport#park() LockSupport.park}
*
*
* A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called Object.wait()
* on an object is waiting for another thread to call
* Object.notify() or Object.notifyAll() on
* that object. A thread that has called Thread.join()
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
*
* - {@link #sleep Thread.sleep}
* - {@link Object#wait(long) Object.wait} with timeout
* - {@link #join(long) Thread.join} with timeout
* - {@link LockSupport#parkNanos LockSupport.parkNanos}
* - {@link LockSupport#parkUntil LockSupport.parkUntil}
*
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
好,看下这几种状态的相互转化吧:
二,接下来看下线程的基本操作:
1,新建线程:这个不用说了,通过继承Thread或者实现Runnable接口两种方式。
2,终止线程:一般来说,线程执行完毕就会结束,无须自动关闭。但是对于一些while true的线程如果想关闭呢?Thread类里有stop()方法,但是已经不建议使用了。因为它是强制停止线程,无论线程处于什么状态,很容易出现线程正在处理一半数据被停止的情况,这样非常容易造成数据不一致问题。所以慎用stop()(最好不用),通过下边这种方式来停止哪些无限循环的线程:
public class StopThread extends Thread {
volatile boolean stopme = false;
//停止方法
public void stopMe() {
stopme = true;
}
@Override
public void run() {
while (true) {
//死循环中如果出现停止标识,则直接跳出
if (stopme) {
System.out.println("exit by stop me");
break;
}
System.out.println(System.currentTimeMillis());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3,线程中断:是一种线程协作机制。是告诉目标线程一个中断通知,至于接到通知后如何处理,则有线程自己决定处理。
public class InterruptThread {
/**
* public void interrupt() 中断线程
* public static boolean interrupted() 判断是否被中断,并清除当前中断标识
* public boolean isInterrupted() 判断是否被中断
*/
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
while (true) {
//判断如果有中断标识,则直接跳出
if (Thread.currentThread().isInterrupted()) {
System.out.println("exit by interrupted");
break;
}
System.out.println(System.currentTimeMillis());
Thread.yield();
}
}
};
t1.start();
Thread.sleep(2000);
//打上中断标识
t1.interrupt();
}
}
4,等待(wait)和通知(notify),也是为了支持线程之间的协作。方法都是在Object上定义的,例如线程A调用了obj.wiat()方法,那么线程A就会停止执行,等到其它线程调用obj.notify()方法为止。
public class WaitNotifyThread {
final static Object ob = new Object();
public static class T1 extends Thread {
@Override
public void run() {
synchronized (ob) {
System.out.println(System.currentTimeMillis() + "t1 is start");
try {
System.out.println(System.currentTimeMillis() + "t1 is wait");
ob.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + "t1 is end");
}
}
}
public static class T2 extends Thread {
@Override
public void run() {
synchronized (ob) {
System.out.println(System.currentTimeMillis() + "t2 is start");
System.out.println(System.currentTimeMillis() + "t2 is notify start");
ob.notify();
System.out.println(System.currentTimeMillis() + "t2 is notify end");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + "t2 is end");
}
}
}
public static void main(String[] args) {
Thread t1 = new T1();
Thread t2 = new T2();
t1.start();
t2.start();
}
}
5,挂起(suspend)和继续执行(resume):也是一对相对的操作,但是JDK也已经标示为废弃方法,不推荐使用。应为使用suspend()去挂起导致线程暂停的同时,并不释放资源,会阻塞其它线程,如果处理不当(例如resume()处理在suspend()之前了,就会导致一直阻塞),所以慎用。我们可以通过wait和notify来像实现stopMe()一样,实现自己一个挂起和继续执行操作。这里不再演示。
6,等待线程结束(join)和谦让(yield):类似现实生活中,我做这件事得等着它做完那件事,我为他让让步等。很容易理解的:
public class JoinThread {
volatile static int i = 0;
public static class AddThread extends Thread{
@Override
public void run() {
for(i=0;i<10000;i++);
}
}
public static void main(String[] args) throws InterruptedException {
AddThread addThread = new AddThread();
addThread.start();
//主线程等待addTread执行完毕
addThread.join();
System.out.println(i);
}
}
三,volatile:非常重要的关键字,如果用volatile修饰,虚拟机就会特殊处理,不能随意变动优化目标指令;并且修改后,各个线程都能看见它的改动,保证变量的可见性。在那个中共享变量是“一写多读”的情况下非常使用,保证修改及时可见。
四,线程组:非常容易理解的概念,如果线程过多,为了方便管理,有了线程组,简单看下使用:
public class ThreadGroupTest implements Runnable {
public void run() {
String groupName = Thread.currentThread().getThreadGroup().getName() + "--" + Thread.currentThread().getName();
while (true){
System.out.println(" i am " + groupName);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ThreadGroup threadGroup = new ThreadGroup("testGroup");
Thread t1 = new Thread(threadGroup,new ThreadGroupTest(),"T1");
Thread t2 = new Thread(threadGroup,new ThreadGroupTest(),"T2");
t1.start();
t2.start();
System.out.println(threadGroup.activeCount());
threadGroup.list();
}
}
五,守护线程(Daemon):是一种特殊的线程,听名字猜想这种线程是系统的守护者。例如jvm的垃圾回收线程等。当一个Java应用中只有守护线程时,也就会自然退出:
public class DaemonDome {
public static class T1 extends Thread {
@Override
public void run() {
while (true) {
System.out.println(" i am live");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t = new T1();
t.setDaemon(true);
t.start();
Thread.sleep(2000);
}
}
六,线程优先级,这个比较好理解,优先级从1到10数字越大优先级越高,只是说概率更高而已,而并非一定。Tread提供三个静态变量:
/**
* 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;
七,线程安全和synchronized:线程安全就是针对共享资源能够高效有序的使用,不要出现同时修改,最终导致数据不一致的问题。而共享资源的同步使用即可解决。synchronized的作用就是实现线程间的同步。前边已经用到,我们不在看例子,总结一下它的用法:1,指定加锁对象:给对象加锁,进入同步代码前要获得给定对象的锁:synchronized(obj);2,直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁:synchronized(this);3,直接作用于静态方法:相当于当前类加锁,进入同步代码前要获得当前类的锁。
好,高并发——Java多线程的复习总结就这样,还是说结合实际生活来学习联想。接下来,我们会继续……