您的位置:首页 > 编程语言 > Java开发

Java多线程导论

2017-02-04 18:25 357 查看

单线程

实际上一个进程就是一个线程,主线程。


多线程

多线程的意思是在主线程之外再新建几个线程,目的是高效率使用cpu的资源。


创建多线程

基本上分为两种创建方式:

继承java.lang.Thread类,然后实例化子类,并且start启动。

实现Runnable接口,Runnable实际上也叫任务,它需要一个线程来执行它,所以需要用new Thread(runnable)来实例化,并且start启动。

这两种方式都必须重写run()方法。这两种方法可以根据自己的业务需求进行选择,对于直接继承Thread类来说,代码会看起来简洁一些,但是由于java单继承的特性,如果直接继承了Thread类,则不能再继承别的类了,这时候如果需要继承其他类,则需要使用Runnable方式。其实还有一种比较特殊的线程创建方式,叫Callable,这是个类似Runnable一样的任务,也需要一个线程来执行,不过跟Runable不同的地方在于它有返回值。执行Callable之后会返回一个Future的类,这个类使用get方法就可以得到返回值了。

线程池

使用ExecutorService pool = Executors.newFixedThread(int size); 来生成一个固定的线程池。

使用ExecutorService pool = Executors.newCachedThreadPool(); 来生成一个可缓存的线程池,当执行execute的时候,如果存在可以使用的线程的时候直接重用以前的线程,如果不存在可以使用的线程,则直接创建一个新的线程,并且添加进入线程池中。旧的线程超过60s未使用就会从线程池中移除。

使用ScheduledExecutorService pool = Executors.newScheduledThreadPool(int corePoolSize); 来生成一个可以周期性之行任务的线程池。

多线程状态

NEW:创建,尚未启动(尚未执行start方法)。

RUNNABLE:可以运行,但需要等待cpu时间片分配资源。

RUNNING:在源码里定义不存在,是我自定义的一个虚拟状态,正在执行的线程状态统称为RUNNING。

BLOCKED:blocked状态是调用Object里面wait()方法产生的,是同步块阻塞或者io阻塞。

WAITING:waiting状态会在调用Object.wait, Thread.join,LockSupport.park这三个方法的情况下出现。当调用Object.wait方法的时候需要另外的线程来执行Object.notify()或者Object.notifyAll()方法来唤醒这个线程,否则这个线程会一直阻塞,而执行join()方法产生的waiting则在该线程结束的时候自动结束waiting状态。

TIME_WAITING:主动waiting一定时间,比如执行Thread.sleep(long millis),Thread.sleep(long millis, int nanos),Object.wait(long millis),Object.wait(long millis, i
4000
nt nanos),Thread.join(long millis),Thread.join(long millis, int nanos)方法即可让线程主动等待一定的时间。

TERMINATED:线程结束,线程结束方法参看下面,结束线程。

这几个状态定义详情参看源码

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:
* <ul>
*   <li>{@link Object#wait() Object.wait} with no timeout</li>
*   <li>{@link #join() Thread.join} with no timeout</li>
*   <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* 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:
* <ul>
*   <li>{@link #sleep Thread.sleep}</li>
*   <li>{@link Object#wait(long) Object.wait} with timeout</li>
*   <li>{@link #join(long) Thread.join} with timeout</li>
*   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
*   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,

/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}


这几种关系如下,



结束线程

1.Thread自身提供stop方法,但是由于潜在的死锁问题目前这个stop方法已经被废弃了。

2.使用共享变量的方式,循环检查共享变量是否要求退出线程,这个共享变量可以使用volatile关键字,目的在于同步这个共享变量,保证每次只有一个线程操作这个变量。同时检查共享变量有个缺点在于,要求线程执行很顺畅,不能阻塞,一旦阻塞,则无法检查共享变量,这时候则可以使用interrupt方法中断阻塞线程。退出阻塞,继续检查共享变量。

关于废弃stop和替代方案详情请参看 《Why Are Thread.stop, Thread.suspend, Thread.resume and Runtime.runFinalizersOnExit Deprecated?》

经常使用的线程方法

start():启动线程。只有执行了start方法,线程才会被启动,才会进入runnable的状态。所以说这个方法是必须要调用的。

run():不需要用户调用,在线程获取cpu时间片之后,线程会自动去执行这个run方法,用户自定的一些功能,都需要重写run方法来实现。

getId():获取线程唯一标识id。

getNamesetName(String Name):获取和设置线程的名字。

getPrioritysetPriority(int priority):获取和设置线程优先级,优先级划分如下,优先级高的线程会优先执行,

/**
* 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;


setDaemon(boolean on):设置守护进程。见守护进程

sleep:有两种方式Thread.sleep(long millis),Thread.sleep(long millis, int nanos),主动让线程休眠一段时间,线程休眠到时候会主动让出cpu的资源,但是并不会释放锁。

join:有三种方式Thread.join(),Thread.join(long millis),Thread.join(long millis, int nanos);第一种不带参数的执行了之后会产生waiting状态,会一直等待到线程结束才会释放。剩下两种在超过指定时间之后会释放。

yield:yield没有时间参数,它的作用和sleep很相像,不过yield的意义在于让相同优先级的线程有机会执行,sleep没有优先级的限制,低优先级的线程也可以执行。yield最准确的理解应该是退让的意思,执行yield方法的意思是我这个线程最主要的工作已经执行完毕了,可以先把时间让给你们执行,执行完了之后我再执行,我不着急。但同时,yield也不会释放锁。

currentThread:返回当前线程的引用,最直接的一个使用是

try {
Thread.currentThread().sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}


多线程同步和死锁

在Java中为了保证多线程的读写数据的一致性,就会需要同步某一块代码或者给多线程操作的某个对象进行加锁。

同步和加锁:synchronized关键字,synchronized的意思是同步的,比如synchronized作用在代码块上,也就是说名这个代码块每次只允许一个线程执行。synchronized关键字可以修饰代码块,方法,类。synchronized关键字作用的方法或对象又分为静态和非静态两种情况。如果它作用的对象是非静态的,那它取得的锁是对象的,如果它作用的对象是静态的,那它取得的锁是类的,这个类所有的实例都共用同一把锁。

synchronized的格式是

synchronized(同步对象){
需要同步的代码块;
}


另外一种保证数据一致的方式:volatile关键字,volatile的意思是不稳定的,也就是在线程访问的时候都会被提示这是不稳定的。所以volatile关键字所修饰的成员变量要求每次在线程访问的时候,必须从共享内存直接读取该成员变量的值,而不是从线程的私有拷贝读取,Java线程私有拷贝参看《Java的内存模型》。正因为不是从私有拷贝访问的而是从共享内存里面访问的,volatile的访问速度会很慢,所以不要在无所谓的地方使用这个关键字。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: