线程的状态
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
在java中要想实现多线程,有两种手段,一种是继承Thread类,另外一种是实现接口.(Runable接口和Callable接口,并与Future、线程池结合使用)。在Java中,推荐使用线程池方式实现多线程。
以下是一个简单的例子
class MyThread extends Thread { @Override public void run() { //TO DO sth. } }
开启线程,调用线程对象的start方法即可,注意不是调用run方法,调用run方法也行,但是就不是新开线程的方式运行了,而是普通的方法调用。真正是调用start() 方法。
public static void main(String[] args) { new MyThread().start(); }
先说一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法:
public interface Runnable { public abstract void run(); }
采用Runnable也是非常常见的一种,我们只需要重写run方法即可。下面也来看个实例。
class MyThread implements Runnable { @Override public void run() { //TO DO sth. } }
在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
public static void main(String[] args) { new Thread(new MyThread()).start(); }
Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做call():
public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
那么怎么使用Callable呢?一般情况下是配合ExecutorService来使用的,也可以结合Thread使用。由于需要返回和取消,因此还需要一个参数来传递状态。
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
Future类位于java.util.concurrent包下,它是一个接口:
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。
FutureTask实现了RunnableFuture接口,我们先来看一下FutureTask的实现:
public class FutureTask<V> implements RunnableFuture<V>
而RunnableFuture又实现了Runnable, Future接口
public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }
出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
所以实现方式
class MyThread implements Callable { @Override public Object call() throws Exception { System.out.println(Thread.currentThread().getName()); return 1; } }
调用可以方式1:使用线程池
public void testCallable1() throws ExecutionException, InterruptedException { ExecutorService pool = Executors.newSingleThreadExecutor(); MyThread task = new MyThread(); FutureTask<Integer> futureTask = new FutureTask<Integer>(task); pool.submit(futureTask); System.out.println(futureTask.get()); }
调用方式2:使用线程对象
MyThread task = new MyThread(); FutureTask<Integer> futureTask = new FutureTask<Integer>(task); Thread thread = new Thread(futureTask).start(); System.out.println(futureTask.get());
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
synchronized关键字的作用域有三种:
1)在实例方法前面加上synchronized关键字,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法。
2)在类方法前面加上synchronized关键字,synchronized static bMethod(){}可以防止多个线程同时访问这个synchronized方法。
3)synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
值得注意的是,synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法。
在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。
ReentrantLock类是可重入、互斥、实现了Lock接口的锁, 它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力
ReenreantLock类的常用方法有:
ReentrantLock() : 创建一个ReentrantLock实例
lock() : 获得锁
unlock() : 释放锁
ReentrantLock lock = new ReentrantLock(); lock.lock(); int syncNum = 1; try { syncNum++ ; } finally { lock.unlock(); }
注:ReentrantLock()还有一个可以创建公平锁的构造方法,但由于会大幅度降低程序运行效率,不推荐使用 。
(1)原子变量实现线程同步
在java的util.concurrent.atomic包中提供了创建了原子类型变量的工具类,使用该类可以简化线程同步。
(2)使用volatile关键字
(3)使用ThreadLocal避免并发问题
(4)util.concurrent下实现了很多线程安全的集合类,可以使用
如果一个线程等待一个永远不会释放的锁,那么线程就会一直无法运行。
只要您拥有多个进程或者线程,而且它们要争用对多个锁的独占访问,那么就有可能发生死锁。如果有一组进程或线程,其中每个都在等待一个只有其它进程或线程才可以执行的操作,那么就称它们被死锁了。
同步由于需要获得锁才能运行,如果多个线程竞争,那么就会导致性能下降。但是需要保证线程同步的正确性,再进行锁的优化。
sleep(long millis): 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
join():指等待t线程终止。
join是Thread类的一个方法,启动线程后直接调用,即join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。
yield():暂停当前正在执行的线程对象,并执行其他线程。
Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
setPriority(): 更改线程的优先级。
线程优先级最大是10,最小是1,默认是5。
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10
interrupt():让出入阻塞的线程抛出一个中断信号
不要以为它是中断某个线程!它只是线线程发送一个中断信号,让线程在无限等待时(如死锁时)能抛出,从而结束线程,但是如果你try了这个异常,那么这个线程还是不会中断的!
注意如果线程没有阻塞,那么是不会抛出InterruptedException的。
setDaemon():指明某个线程是守护程序线程
守护程序线程作为在程序中创建的后台线程,如果程序中所有的非守护线程执行完成,那么程序就会推出。例如垃圾回收的线程等也是后台线程。
Obj.wait(),与Obj.notify()
Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,与notify是针对已经获取了Obj锁进行操作。
从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){…}语句块内。从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。
ref.
https://blog.csdn.net/evankaka/article/details/44153709