进行Java 并发设计的第一步要了解Java中为线程提供的API。比如如何新建、启动、终止、中断、等待、通知等。
首先了解一下一个线程的几种状态
线程状态在Java中是通过一个Thread的内部枚举State标识的。
NEW---->RUNNABLE---->BLOCKED---->WAITING---->TIMED_WAITING---->TERMINATED
1、创建状态(Thread.State.NEW)
如果只是创建了一个线程,而没有启动它(start)则线程状态为创建状态。
例如:Thread thread1 = new Thread();
处于创建状态的线程还没有获得应有的资源,所以,这是一个空的线程。线程只有通过启动后,系统才会为它分配资源。
2、可运行状态(Thread.State.RUNNABLE)
通过调用t.start()启动一个线程,使该线程进入可运行(Thread.State.RUNNABLE)的状态。处于该状态的线程进入就绪队列中,当线程获取CPU的时间片则开始运行。
3、阻塞状态(Thread.State.BLOCKED)
受阻塞并且正在等待监视器锁的某一线程的线程状态。处于受阻塞状态的某一线程正在等待监视器锁,以便进入一个同步的块/方法,或者在调用 Object.wait 之后再次进入同步的块/方法。
4、等待状态(Thread.State.WAITING、TIMED_WAITING)
某一等待线程的线程状态。某一线程因为调用下列方法之一而处于等待状态:
不带超时值的 Object.wait
不带超时值的 Thread.join
LockSupport.park
处于等待状态的线程正等待另一个线程,以执行特定操作。 例如,已经在某一对象上调用了 Object.wait() 的线程正等待另一个线程,以便在该对象上调用 Object.notify() 或 Object.notifyAll()。已经调用了 Thread.join() 的线程正在等待指定线程终止。
TIMED_WAITING具有指定等待时间的某一等待线程的线程状态。某一线程因为调用以下带有指定正等待时间的方法之一而处于定时等待状态:
Thread.sleep
带有超时值的 Object.wait
带有超时值的 Thread.join
LockSupport.parkNanos
LockSupport.parkUntil
5、结束状态(Thread.State.TERMINATED)
新建一个线程有三种方式
/**
* 线程初始化过程:
* 1. new Thread()过程中会为线程设置线程组、执行目标、线程名、线程栈大小
* 默认和主线程一个组;Thread 实现了Runnable,本身即为一个执行目标
* 2. 真正创建线程是在start()中,这里使用了本地调用,通过C代码初始化线程需要的系统资源。
* 此时start()的这个线程处于就绪状态,当得到CPU的时间片后就会执行其中的run()方法。
* 3. 线程从创建到最终的消亡,要经历若干个状态。
* 一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、等待(time waiting、waiting)、消亡(dead)。
*
*/
public class ThreadNewWays {
public static void main(String[] args){
firstWay();
secondWay();
thirdWay();
}
/**
* 继承Thead类重写run方法, Thread实现了Runnable接口,本身就是一个执行任务
*/
public static void firstWay() {
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("this is first way to new thread!");
}
};
thread.start();
}
/**
* 通过实现Runnable接口告诉线程执行什么任务
*/
public static void secondWay() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("this is second way to new thread!");
}
}).start();
}
/**
* 通过线程池创建线程
*/
public static void thirdWay() {
ExecutorService service = new ThreadPoolExecutor(1, 1, 0L,
TimeUnit.SECONDS, new LinkedBlockingQueue());
service.submit(new Runnable() {
@Override
public void run() {
System.out.println("this si third way to new thread !");
}
});
service.shutdown();
}
}
一般来说,线程在执行完之后会自动关闭,可是一些服务端的线程可能需要手动关闭。Thread中的stop方法已经被弃用,stop方法关闭线程太过暴力,强行把执行到一半的线程关闭可能导致数据不一致的问题。可通过一个volatile变量来达到关闭线程的目的。
public class ThreadStop {
public static User user = new User();
public static void main(String[] args){
Thread thread1 = new Thread(new ReadObjectThread());
thread1.start();
while (true) {
ChangeObjectThread thread2 = new ChangeObjectThread();
thread2.start();
try {
Thread.sleep(110);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* stop 过于暴力,会破坏数据
* 可通过一个volatile变量来达到关闭线程的目的
*/
// thread2.stop();
thread2.stopMe();
}
}
public static class ChangeObjectThread extends Thread {
volatile boolean stopMe = false;
public void stopMe(){
this.stopMe = true;
}
@Override
public void run() {
while (true) {
if (stopMe) {
System.out.println("stop thread");
break;
}
synchronized (user) {
int v = (int) (System.currentTimeMillis()/1000);
user.setId(v);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
user.setName(String.valueOf(v));
}
Thread.yield();
}
}
}
public static class ReadObjectThread implements Runnable{
@Override
public void run() {
while (true) {
synchronized (user) {
if (user.getId() != Integer.valueOf(user.getName())) {
System.out.println(user);
}
}
Thread.yield();
}
}
}
public static class User{
private int id;
private String name;
public User() {
this.id = 0;
this.name = "0";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
}
Java中,线程中断是一种重要的线程间协作机制。线程中断并不会立即停止,而是给线程发一个通知告知目标线程应该退出,至于目标线程接到通知之后如何处理,则完全由目标线程决定。所以一般目标线程中会去处理中断退出。
public class ThreadInterrupt {
public static void main(String[] args) throws Exception{
// noDealInterrupt();
// dealInterrupt();
dealInterruptException();
}
/**
* 中断异常处理
* @throws Exception
*/
public static void dealInterruptException () throws Exception {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Thread is interrupt");
break;
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("Interrupt when sleep");
//Thread.sleep()方法由于中断而抛出异常,此时,他会清除中断标记,所以在异常处理中,再次设置中断标记。
Thread.currentThread().interrupt();
}
Thread.yield();
}
}
});
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
/**
* 处理中断
* @throws Exception
*/
public static void dealInterrupt() throws Exception {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("thread is interrupt");
break;
}
Thread.yield();
}
}
});
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
/**
* 没有处理中断
* @throws Exception
*/
public static void noDealInterrupt() throws Exception{
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
Thread.yield();
}
}
});
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
}
注意:
- 无论是wait还是notify必须包含在对应的synchronized语句中,首先获取到目标对象的一个监视器。如果一个线程调用了object.wait(),那么它就会进入object对象的等待队列。
- Object.wait()和Thread.sleep()都可以让线程等待若干时间,wait可以被唤醒和释放目标对象的锁,Thread.sleep()不会释放任何资源。
public class ThreadWaitNotify {
private static Object obj = new Object();
public static void main(String[] args){
Thread t1 = new T1();
Thread t2 = new T2();
t1.start();
t2.start();
}
public static class T2 extends Thread {
public void run() {
synchronized (obj) {
System.out.println("T2 start " + System.currentTimeMillis());
obj.notify();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("T2 end " + System.currentTimeMillis());
}
}
}
public static class T1 extends Thread {
public void run() {
synchronized (obj) {
System.out.println("T1 start " + System.currentTimeMillis());
try {
System.out.println("T1 wait for Object");
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("T1 end " + System.currentTimeMillis());
}
}
}
}
public class ThreadJoinYield {
public volatile static int i = 0;
public static void main(String[] args) throws Exception{
Thread thread = new AddThread();
thread.start();
//阻塞当前线程,即main线程,直到AddThread线程执行完毕
thread.join();
System.out.println(i);
}
public static class AddThread extends Thread {
public void run() {
System.out.println("AddThread started ...");
for (i=0;i<1000000;i++) {
if (i%50000==0) {
System.out.println("thread yield cpu");
//使当前线程让出CPU时间
Thread.yield();
}
}
}
}
}
先建线程的时候可以指定线程组,把功能相同的线程放置在一个线程组里。
public Thread(ThreadGroup group, Runnable target, String name)
thread.setDaemon(true);
注意:
一个Java应用只有守护线程时,Java虚拟机就会自然退出。
设置守护线程必须在start()方法之前设置
优先级高的线程在竞争资源时会更有优势,更可能抢占资源,这也只是一个概率事件。
Java中使用1到10表示线程的优先级,数字越大优先级越高。