Java多线程
为了以后更好地学习并发编程,现阶段将回顾学习java的多线程,稳固Java基础。
线程的创建
创建线程非常简单,Java提供了3种方式。第一种直接继承Java的Thread类,然后使用start方法进行线程启动。但是Java是单继承的,直接继承的方式显然不够优雅。Java提供的第二种方式,实现Runnable接口,通过Runnable的方法来创建线程,实际上是等价于继承Thread实现的,只不过实现接口的方式需要使用一个Thread来执行这个Runnable。当我们需要在线程执行完后提供一个返回值,那么我们就需要使用第三种方式,实现Callable接口,这个Callable接口使用了泛型,运行Callable的时候使用的不是Thread,而是FutureTask,当线程执行完毕后,我们便可以通过FutureTask的get方法来获取其返回值。具体代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class ThreeWayRunThread {
static class ExtendWay extends Thread { @Override public void run() { System.out.println("ExtendWay Thread is running!!"); } }
static class RunnableWay implements Runnable { public void run() { System.out.println("RunnableWay Thread is running!!"); } }
static class CallableWay implements Callable<String> {
public String call() throws Exception { return "CallableWay Thread is Running!! This is it's return parameter."; } }
public static void main(String[] args) { ExtendWay extendWay = new ExtendWay(); RunnableWay runnableWay = new RunnableWay(); CallableWay callableWay = new CallableWay();
Thread thread = new Thread(runnableWay); FutureTask<String> futureTask = new FutureTask<String>(callableWay);
extendWay.start(); thread.start(); futureTask.run();
try { System.out.println(futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }
}
}
|
安全地中断线程
JDK废弃了使用stop方法的方式来强制中断线程,取代的是interrupt方法。interrupt方法是以中断标记的方式来进行干涉,使得Java的线程更贴切协作式。当线程抛出InterruptException的时候需要手动抓取调用interrupt方法来重新设置中断标记,否则线程将不会中断。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| public class StopThread {
static class InterruptThread extends Thread { @Override public void run() { while (!isInterrupted()) { System.out.println("thread is running!"); } System.out.println(this.isInterrupted()); } }
static class InterruptRunnable implements Runnable {
public void run() { while (!Thread.currentThread().isInterrupted()) { System.out.println("runnable is running!"); } System.out.println(Thread.currentThread().isInterrupted()); } }
static class InterruptOccurException implements Runnable {
public void run() { while (!Thread.currentThread().isInterrupted()) { System.out.println("runnable is running!"); try { Thread.sleep(100); } catch (InterruptedException e) { System.out.println("Occur Exception"); Thread.currentThread().interrupt(); e.printStackTrace(); } } System.out.println(Thread.currentThread().isInterrupted()); } }
public static void main(String[] args) throws InterruptedException { InterruptThread interruptThread = new InterruptThread(); InterruptRunnable interruptRunnable = new InterruptRunnable(); InterruptOccurException interruptOccurException = new InterruptOccurException();
interruptThread.start(); Thread.sleep(20); interruptThread.interrupt();
Thread runnableThread = new Thread(interruptRunnable); runnableThread.start(); Thread.sleep(20); runnableThread.interrupt();
Thread occurException = new Thread(interruptOccurException); occurException.start(); Thread.sleep(500); occurException.interrupt();
} }
|
线程之间的协作
wait和notify/notifyAll
wait方法使当前线程进入等待,notify方法唤醒一个等待线程,notifyAll方法唤醒所以等待线程。需要注意的是,wait和notify、notifyAll都需要获取锁对象,如果没有加锁,则会抛出异常。使用wait方法时,wait方法将会把持有的锁释放,而notify/notifyAll方法调用时,本身是不会释放锁的,它将会等待该方法完成后再将锁释放,所以通常都会将notify/notifyAll放在最后执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| package com.wentao.study.basic.thread;
import com.wentao.study.basic.thread.utils.SleepUtils;
public class ThreadLifeCircle {
private static class Pack { int km; String target;
public synchronized void moving() { this.km = 100; notifyAll(); }
public synchronized void arrived() { this.target = "Shanghai"; notifyAll(); }
public synchronized void waitTarget() { while (!"Shanghai".equals(target)) { try { wait(); System.out.println("check target..." + Thread.currentThread().getId()); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("target arrive"); }
public synchronized void waitKm() { while (this.km < 100) { try { wait(); System.out.println("check km..." + Thread.currentThread().getId()); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("km end...."); } }
public static void main(String[] args) { final Pack pack = new Pack(); pack.km = 0; pack.target = "Beijing"; for (int i = 0; i < 3; i++) { new Thread(new Runnable() { public void run() { pack.waitTarget(); } }).start(); } for (int i = 0; i < 3; i++) { new Thread(new Runnable() { public void run() { pack.waitKm(); } }).start(); } SleepUtils.second(1); pack.moving(); }
}
|
join
面试点:如何保证线程A在线程B执行完后再执行?使用join方法。可以通过join方法将线程串联起来。yield方法执行后,其持有的锁是不释放的,sleep方法也不会释放锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class Join { private Thread prev;
public Join(Thread prev, String name) { this.prev = prev; this.setName(name); }
@Override public void run() { System.out.println("this is " + this.getName()); try { prev.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.getName() + " is end"); } public static void main(String[] args) { Thread pre = Thread.currentThread(); for (int i = 0; i < 10; i++) { pre = new Join(pre, String.valueOf(i)); pre.start(); } SleepUtils.second(2); System.out.println("main thread shut down"); } }
|
synchronized、volatile和ThreadLocal
synchronized是Java提供的实现多线程同步锁。其中可分为对象锁和类锁,即对对象加锁和对类加锁。synchronized关键字可用于方法、静态方法、代码块。同步锁保证了多个线程能够互斥执行代码块。简单使用代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class Synchronized {
static class First implements Runnable { private Synchronized sync; private String name;
public First(Synchronized sync, String name) { this.sync = sync; this.name = name; }
public void run() { while (!Thread.currentThread().isInterrupted()) { SleepUtils.second(1); sync.objectPrint(name); SleepUtils.second(1); Synchronized.classPrint(name); } } }
static synchronized void classPrint(String s) { SleepUtils.second(1); System.out.println("class print is running --- " + s); SleepUtils.second(1); System.out.println("class print is end --- " + s); }
synchronized void objectPrint(String s) { SleepUtils.second(1); System.out.println("object print is running --- " + s); SleepUtils.second(1); System.out.println("object print is end --- " + s); }
public static void main(String[] args) { Synchronized sync = new Synchronized(); Thread fThread = new Thread(new First(sync, "1")); Thread sThread = new Thread(new First(sync, "2"));
fThread.start(); sThread.start(); } }
|
volatile是java中的最轻量级的同步机制,当一个线程改变volatile变量时,会告诉虚拟机将这个变量值刷到主内存中,其他线程中保存的副本将会失效,线程将从主内存中重新读取变量值。其最常见的用途是一个线程写,多个线程读,能确保变量的原子性。volatile并不是线程安全的,它只能保证变量的可见性,不能保证其原子性。如下代码所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class Volatile {
private static class VolatileThread implements Runnable {
private volatile int number = 0;
@Override public void run() { String name = Thread.currentThread().getName(); number = number + 1; System.out.println(name + ": " + number); SleepUtils.mile(100); number = number + 1; System.out.println(name + ": " + number); } }
public static void main(String[] args) { VolatileThread v = new VolatileThread(); Thread t1 = new Thread(v); Thread t2 = new Thread(v); Thread t3 = new Thread(v); Thread t4 = new Thread(v); Thread t5 = new Thread(v);
t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
|
ThreadLocal
是线程级别的变量,每个线程拥有其自己的变量,线程与线程间互不干扰。由于其每个线程都拥有一个副本,其占有资源将会是非常庞大的,所以ThreadLocal
尽量存储比较小的数据。演示代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class MyThreadLocal {
public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return 1; } };
public static class ThreadLocalTest { public void start() { Thread[] arr = new Thread[3]; for (int i = 0; i < 3; i++) { arr[i] = new Thread(new ThreadLocalRunnable(i)); } for (int i = 0; i < 3; i++) { arr[i].start(); } } }
public static class ThreadLocalRunnable implements Runnable {
private int id;
ThreadLocalRunnable(int id) { this.id = id; }
@Override public void run() { Integer integer = threadLocal.get(); integer += id; threadLocal.set(integer); SleepUtils.second(1); System.out.println("Thread-" + id + " : " + threadLocal.get()); } }
public static void main(String[] args) { new ThreadLocalTest().start(); }
}
|