第二章 java并行程序基础

2.1 线程必须要知道的事

进程:是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

线程:是轻量级进程,是程序执行的最小单位。

一个线程的生命周期

线程生命周期

2.2初始线程

2.2.1 新建线程

Thread t1 = new Thread();

t1.start();

线程Thread,有一个方法run()方法。start()方法就会新建一个线程并让这个线程执行run()方法。

Thread t1 = new Thread();

t1.run();

以上代码不能新建一个线程,而是在当前线程中调用run()方法,只是一个普通的方法调用。

Thread t1 = new Thread(){
       @Override
       public void run(){
           System.out.println("hello ,I am t1");
       }
   };
t1.start();

上述代码使用了内部匿名类,重写了run()方法;也可以使用Runnable接口来实现同样的操作。

public interface Runnable(){
       public abstract void run();
}

Thread类中的构造方法:

public Thread(Runnable target)

它传入一个Runnable接口的实例,在start()方法调用时,新的线程就会执行Runnable.run()方法,默认的Thread.run()方法如下:

public void run(){
        if(target != null){
        target.run();
    }
}

具体实现如下:

public class CreateThread implements Runnable{
    public static void main(String args[]){
        Thread t1 = new Thread(new CreateThread());
        t1.start();
    }

    @Override
    public void run(){
        System.out.println("i am run");
    }
}

2.2.2 终止线程

不要随便使用stop()方法来终止一个线程。
原因:stop()方法会直接终止线程,并立即释放这个线程所持有的锁,而锁恰恰是用来维护对象的一致性的。
例如:写线程写入数据写到一半,强行stop(),那么对象就会被写坏,但由于锁已经释放,其他读线程就会读出错误的数据。

我们可以使线程在合适的地方退出

public class ChangeObjectThread extends Thread {
    volatile boolean stopme = false;
    
    public void stopme(){
        stopme = true;
    }
    
    public void run(){
        while(true){
            //判断线程是否退出,在此阶段退出
            if(stopme){
                System.out.println("exit by stop me");
                break;
            }
            //修改对象数据
            synchronized (u){
                u.setId();
                u.setName();
            }
            Thread.yield();
        }
    }
}

上述代码标记了一个变量stopme,用于指示线程是否需要退出。使用这种退出方式,线程就没有机会在写坏数据了,因为它总是在一个合适的时间终止进程。

2.2.3 线程中断

线程中断不会使线程立即退出,而是给线程发一个通知,告知目标线程,至于何时退出,则由线程自己选择一个合适的时间。
3个线程中断有关的方法

public void Thread.interrupt()        //中断线程
public boolean Thread.isInterrupted()         //判断线程是否中断
public static boolean Thread.interrupted()           //判断是否被中断,并判断当前中断状态

中断示例:

public class Test  {
    public static void main(String args[]) throws InterruptedException {
        Thread t1 = new Thread(){
          public void run(){
              while(true){
                  //判断线程是否中断
                  if(Thread.currentThread().isInterrupted()){
                      System.out.println("Interrupted!");
                      break;
                  }
                  Thread.yield();
              }
          }
        };
        t1.start();
        Thread.sleep(2000);
        t1.interrupt();
    }
}

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

wait()方法和notify方法是在Object类中的,意味这任何类都可以调用这两个方法。
这两个方法的声明如下:

public final void wait() throws InterruptedException
public final native void notify()

在线程中,如果调用了obj.wait()方法,线程就会停止执行,进入一个等待队列,直到其他线程调用了obj.notify()方法,从等待队列中随机选择一个线程唤醒。
Object.wait()必须包含在对应的synchronized语句中,无论是wait()还是notify()都必须获得目标对象的监视器。

wait和notify的工作流程

一个小实例:

package chapter2;

public class SimpleWN {
    final static Object object = new Object();
    public static class T1 extends Thread{
        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{
        public void run(){
            synchronized (object){
                System.out.println(System.currentTimeMillis()+":T2 start,notify one thread");
                object.notify();
                System.out.println(System.currentTimeMillis()+":T2 end");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String args[]){
        Thread t1 = new T1();
        Thread t2 = new T2();
        t1.start();
        t2.start();
    }
}

运行结果

2.2.5 挂起(suspend)和继续执行(resume)

现已被标注为废弃方法,不推荐使用。
原因:suspend方法在导致线程暂停的同时,不会释放任何锁资源,使得其他需要访问此资源的线程都无法正常运行。

2.2.6 等待线程结束(join)和谦让(yeild)

一个线程的输入可能非常依赖于另一个或多个线程的输出,这时就要用到join()。
join的两个方法:

public final void join() throws InterruptedException
public final synchronized void join(long mills) throws InterruptedException

其中第一个方法表示无限等待,会一直阻塞当前线程,直到目标线程执行完毕。
第二个方法给出了一个最大等待时间,如果超出时间,那么线程将不再等待。
一个小实例:

package chapter2;

public class JoinMain {
    public volatile static int i = 0;
    public static class AddThread extends Thread{
        @Override
        public void run() {
            for(i = 0;i < 100000000; i++);
        }
    }
    
    public static void main(String args[]) throws InterruptedException {
        AddThread at = new AddThread();
        at.start();
        at.join();
        System.out.println(i);
    }
}

在执行at.join后,i总是等于100000000,说明主线程等待at线程执行完成后,再继续执行。

2.3 volatile关键字

volatile声明一个变量,就等于告诉了虚拟机,这个变量极有可能会被某些程序或线程修改。
根据编译器的优化规则,如果不使用volatile关键字,那么变量修改后,其他线程可能并不会通知到。

2.4 线程组

在一个系统中,可以将相同功能的线程放置在同一个线程组中。
一个小实例:

package chapter2;

public class ThreadGroupName implements Runnable{
    public static void main(String args[]){
        ThreadGroup tg = new ThreadGroup("PrintGroup");
        Thread t1 = new Thread (tg,new ThreadGroupName(),"T1");
        Thread t2 = new Thread (tg,new ThreadGroupName(),"T2");
        t1.start();
        t2.start();
        //获得当前组活动线程总数
        System.out.println(tg.activeCount());
        tg.list();
    }

    @Override
    public void run() {
        String groupAndName = Thread.currentThread().getThreadGroup().getName()+"-"+Thread.currentThread().getName();
        while(true){
            System.out.println("I am "+groupAndName);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
运行结果

2.5 守护线程(Daemon)

守护线程提供一些系统性的服务,比如垃圾回收器线程和JIT线程。当用户线程全部结束,只有守护线程时,应用程序应该结束,此时java虚拟机将会退出。
一个小实例:

package chapter2;

public class DaemonDemo {
    public static class DaemonT extends Thread{
        @Override
        public void run() {
            while(true){
                System.out.println("I am alive");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String args[]) throws InterruptedException {
        Thread t = new DaemonT();
        t.setDaemon(true);//设置t为守护线程
        t.start();

        Thread.sleep(3000);
    }
}

当主线程mian休眠3秒后退出时,t也退出了。

2.6 线程优先级

在java中,使用1到10表示线程优先级。一般可以使用三个内置静态变量表示

public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;

数字越大,优先级越高
通过setPriority()方法来设定线程的优先级。

2.7 synchronized关键字

关键字synchronized的作用是实现线程间的同步。它的工作是对同步代码的加锁,使得每一次只能有一个线程进入同步块,从而保证线程间的安全。
synchronized的各种用法:
指定对象加锁:进入同步代码前要获得给定对象的锁。
直接作用于实例方法:进入同步代码前获得当前实例的锁。
直接作用于静态方法:进入同步代码前要获得当前类的锁。

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