java中关于线程的一些API

java中关于线程的一些API:

* 线程的启动

    线程的启动有两种方式:继承Thread类,复写run方法;实现Runnable接口,实现run方法。

    两种方式都可以,但切记继承Thread类时如果要启动线程需要调用的是start方法而不是run方法,如果调用的是run方法的话不会开启新线程,只会在当前线程中执行run方法中的代码,如下面的代码

public class ThreadStart {

public static void main(String[] args) {

System.out.println(Thread.currentThread().getName());

ThreadRun t = new ThreadRun();

t.run();

//t.start();

}

}

class ThreadRun extends Thread{

@Override

public void run(){

System.out.println(Thread.currentThread().getName());

}

}

上面的代码,如果调用的是run方法,得到的线程名字都是main线程,如果调用的是start方法得到的就是两个线程名字

* 线程中断

    关于线程中断有三种方式:

    public void interrupt//中断

    public static boolean interrupted()//判断是否中断并清除中断标志位

    public boolean isInterrupted//判断是否中断

关于中断和Thread中的stop方法是有区别的,调用stop方法是立刻停止线程,这样可能会造成数据不一致的现象,而调用interrupt方法是给当前线程添加中断标志,但不一定会立即中断

第一个是实例方法,它通知目标线程中断,也就是设置中断标志位。中断标志位表示当前线程已经被中断了;第三个也是实例方法,它判断目标线程是否有被中断;第二个是静态方法,也是用来判断是否有被中断,但它会清除目标线程的中断标志位。

public class ThreadInterruptDemo1 {

public static void main(String[] args)throws InterruptedException {

Thread t = new Thread(){

@Override

public void run(){

while (true){

Thread.yield();

}

}

};

t.start();

Thread.sleep(2000);

t.interrupt();

}

}

在上述代码中,虽然对t进行了中断,但是线程并没有进行中断,如果想要中断需要进一步处理代码:

public class ThreadInterruptDemo1 {

public static void main(String[] args)throws InterruptedException {

Thread t = new Thread(){

@Override

public void run(){

while (true){

if(Thread.currentThread().isInterrupted()){

System.out.println("thread is interrupted");

break;

}

Thread.yield();

}

}

};

t.start();

Thread.sleep(2000);

t.interrupt();

}

}

这样就会中断退出。如果在循环体中出现类似wait或者sleep等操作时,只能通过中断来识别

public class ThreadInterruptDemo1 {

public static void main(String[] args)throws InterruptedException {

Thread t = new Thread(){

@Override

public void run(){

while (true){

if(Thread.currentThread().isInterrupted()){

System.out.println("thread is interrupted");

break;

}

try {

Thread.sleep(2000);

}catch (InterruptedException e){

System.out.println("Interrupt when sleep");

//设置中断标志

Thread.currentThread().interrupt();

}

Thread.yield();

}

}

};

t.start();

Thread.sleep(2000);

t.interrupt();

}

}

Thread.sleep()方法由于中断而抛出的异常,它会清除中断标志,如果不加处理,那么在下一次循环中就无法捕获到这个中断标志位。

* 等待(wait)和通知(notify)

    wait和notify必须使用在synchronized代码块中,当一个线程调用了wait方法时,这个线程就会进入等待状态并且会释放锁,当另外一个线程调用notify时,这个线程才会被唤醒(假设此处只有两个线程,并且等待队列中只有这一个线程)。

注意事项:wait和sleep方法都是让线程进行等待,最重要的区别是wait方法会释放当前锁,而sleep不会;线程调用notify方法之后并不会立刻释放锁,只有当notify之后的代码执行完毕之后才会释放锁。

public class ThreadWaitAndNotify {

private static Object object = new Object();

public static class T1 extends Thread{

@Override

public void run(){

synchronized (object){

System.out.println(System.currentTimeMillis()+" :T1 start!");

try {

System.out.println(System.currentTimeMillis()+" :T1 wait for object");

object.wait();

}catch (InterruptedException e){

e.printStackTrace();

}

System.out.println(System.currentTimeMillis()+" :T1 end!");

}

}

}

public static class T2 extends Thread{

@Override

public void run(){

synchronized (object){

System.out.println(System.currentTimeMillis()+" :T2 start!");

object.notify();

System.out.println(System.currentTimeMillis()+" :T2 end!");

try {

Thread.sleep(2000);

}catch (InterruptedException e){

}

}

}

}

public static void main(String[] args) {

Thread t1 = new T1();

Thread t2 = new T2();

t1.start();

t2.start();

}

}

运行结果如下:

1512288311968 :T1 start!

1512288311968 :T1 wait for object

1512288311969 :T2 start!

1512288311969 :T2 end!

1512288313970 :T1 end!

* 等待线程结束(join)和谦让(yield)

很多时候,一个的输入可能非常依赖另外一个或多个线程的输出,此时,这个线程就需要等待依赖线程执行完毕,才能继续执行。JDK提供了两个方法:

public final synchronized void join(long millis)//最大等待时间,当超过这个时间主线程就会继续执行

public final void join()//无限期等待,直到目标线程执行完毕

yield:

public static native void yield()

这是一个静态方法,一旦执行,它会使当前线程让出CPU,但要注意,让出CPU并不表示当前线程不执行了。当前线程在让出CPU后,还会进行CPU资源的争夺。

* 守护线程(daemon)

    当一个应用中只有守护线程时,表示这个java虚拟机就会自然退出

注意事项:设置守护线程时一定要设置在start方法之前,否则会报IllegalThreadStatException异常,报出这个异常并不会使线程停止,该线程还会继续执行。

* 线程优先级

    在Thread类中有表示线程优先级的字段

    public final static int MIN_PRIORITY = 1;

    public final static int NORM_PRIORITY = 5;

    public final static int MAX_PRIORITY = 10;

    数值越大,优先级越高。

* 线程安全(synchronized)

    并行程序的开发是以线执行结果的争取性为保证的,所以线程安全是并行程序开发额根本和根基。以下这个程序就是典型的线程不安全

public class ThreadSynchronizeDemo1 implements Runnable {

static ThreadSynchronizeDemo1 instance = new ThreadSynchronizeDemo1();

static volatile int i = 0;

public void increase(){

i++;

}

@Override

public void run(){

for(int j = 0; j < 1000000; j++){

increase();

}

}

public static void main(String[] args)throws InterruptedException {

Thread t1 = new Thread(instance);

Thread t2 = new Thread(instance);

t1.start();

t2.start();

t1.join();

t2.join();

System.out.println(i);

}

}

上述的结果会小于2000000,线程t1和t2同时读取i为0,并各自计算得到i=1,并先后写入这个结果,因此,虽然i++被执行了2次,但是实际i的值只增加了1,想解决这个问题synchronized关键字是一个解决方案。

synchronized可以有多种用法:

    [x] 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁

    [x] 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁

    [x] 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁

这时对上述代码进行改造:

public synchronized void increase(){

i++;

}

在这个实例方法上加synchronized关键字进行同步

在上述代码尤其要特别注意:在创建线程时使用的是同一个对象实例,如果此时使用的两个不同的对象实例,在increase()方法上加synchronized关键字是不起作用的,如果想得到正确的结果而又是不同的对象实例,可以在increase()方法之前加上static关键字,让这个方法变为静态方法,这个synchronized关键字就会作用于这个类上。

* 并发下的list

    JDK中的util包中的list是线程不安全的类,如下面的代码:

public class ThreadList {

static List<Integer> list = new ArrayList<>();

static class AddThread implements Runnable{

@Override

public void run(){

for(int i = 0; i <1000000; i++){

list.add(i);

}

}

}

public static void main(String[] args) throws InterruptedException {

Thread t1 = new Thread(new AddThread());

Thread t2 = new Thread(new AddThread());

t1.start();

t2.start();

t1.join();

t2.join();

System.out.println(list.size());

}

}

上面的代码可能会出现三种情况:

第一种:程序正常结束

第二种:抛出异常:Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 15  这是因为ArrayList在扩容过程中,内部一致性被破坏,但由于没有锁的保护,另外一个线程访问到了不一致的内部状态,导致出现越界问题。

第三种:程序正常结束,但是结果不正确。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容