《Java编程那点事儿》读书笔记(七)——多线程

1.继承Thread类

通过编写新的类继承Thread类可以实现多线程,其中线程的代码必须书写在run方法内部或者在run方法内部进行调用。

public class NewThread extends Thread {
    private int ThreadNum;

    public NewThread(int ThreadNum){
        this.ThreadNum = ThreadNum;
    }

    public void run(){
        try{
            for(int i = 0;i < 10;i ++){
                Thread.sleep(1000);
                System.out.println("running Thread"+ThreadNum+":"+i);
            }
        }catch(Exception e){

        }
    }
}

上述代码定义了新线程NewThread,并在run中实现输出十个数的功能。用以下代码在main函数中调用:

NewThread nt = new NewThread(1);
nt.start();

NewThread nt2 = new NewThread(2);
nt2.start();

得到的结果如下:

running Thread1:0
running Thread2:0
running Thread1:1
running Thread2:1
running Thread1:2
running Thread2:2
running Thread1:3
running Thread2:3
running Thread1:4
running Thread2:4
running Thread1:5
running Thread2:5
running Thread1:6
running Thread2:6
running Thread1:7
running Thread2:7
running Thread1:8
running Thread2:8
running Thread1:9
running Thread2:9

可以看到启动的两个线程并行运行。

2.实现Runnable接口(Java.lang.Runnable)

public class MyRunnable implements Runnable {
    private int ThreadNum;

    public MyRunnable(int ThreadNum){
        this.ThreadNum = ThreadNum;
    }
    public void run(){
        try{
            for(int i = 0;i < 10;i ++){
                Thread.sleep(1000);
                System.out.println("running Thread"+ThreadNum+":"+i);
            }
        }catch(Exception e){

        }
    }
}

在main中调用接口:

MyRunnable mr = new MyRunnable(1);
Thread t = new Thread(mr);

MyRunnable mr2 = new MyRunnable(2);
Thread t2 = new Thread(mr2);

t.start();
t2.start();

3.Timer & TimerTask组合实现多线程

public class TimerAndTimerTask extends TimerTask{
    private String s;
    public TimerAndTimerTask(String s){
        this.s = s;
    }

    public void run(){
        try{
            for(int i = 0;i < 10;i ++){
                Thread.sleep(1000);
                System.out.println("running Thread"+s+":"+i);
            }
        }catch(Exception e){

        }
    }
}

在main函数中创建线程代码如下:

Timer t = new Timer();
Timer t2 = new Timer();

TimerAndTimerTask tatt = new TimerAndTimerTask("1");
TimerAndTimerTask tatt2 = new TimerAndTimerTask("2");

t.schedule(tatt, 0);
t2.schedule(tatt2, 0);

上述代码中的要用两个Timer启动不同的线程,它们才能同时运行。如果只用一个Timer,则一次只能启动一个线程。

上述中的schedule方法一共有四种多态:

public void schedule(TimeTask task, Date time)

//在2009年10月1日10点0分0秒启动该线程或超过该时间也启动线程
Date d = new Date(2009-1900,10-1,1,10,0,0);
t.schedule(task,d);
public void schedule(TimerTask task,Date firstTime,long period)

//到达或者超过2009年10月1日10点时候每隔20000ms就启动一次线程,这种方式会重复触发线程
Date d = new Date(2009-1900,10-1,1,10,0,0);
t.schedule(task,d,20000);
public void schedule(TimerTask task,long delay)

//执行启动代码1000ms后启动线程
t.schedule(task,1000);
//在delay ms后启动线程,并且每隔period ms启动一次
public void schedule(TimerTask task,long delay,long period)

在eclipse里面试了一下最后一种,会不断的输出0~9这10个数,因为用的Timer只有一个,但是每过1s就触发一次线程,所以不停的有线程需要执行。

4.互斥

synchronized,修饰方法或代码块,表示如果两个或者以上的线程同时执行该代码段时,如果一个线程已经开始执行该代码段,则另外一个线程必须等待这个线程执行完这段代码后才能执行。

public class Toilet {
    public synchronized void enter(String name){
        System.out.println(name+" enters the toilet!");
        try{
            Thread.sleep(2000);
        }catch(Exception e){}
        System.out.println(name + " has left the toilet!");
    }
}

public class Human extends Thread{
    private Toilet t;
    private String s;
    public Human(String s,Toilet t){
        this.s = s;
        this.t = t;
        start();
    }

    public void run(){
        t.enter(s);
    }
}

在main中创建Human线程的代码如下,一共创建了三个Human线程:

Toilet t = new Toilet();
Human t1 = new Human("Ann", t);
Human t2 = new Human("Jeff", t);
Human t3 = new Human("Joe", t);

在上述Toilet类中有一段互斥的代码,输出当前进入厕所的人,并且在1s后离开。

在类Human中,在构造函数里面开启进程,所以每当new一个Human类就相当于开启了一个线程,但是会不会立即执行run函数要看系统中是否已经有在跑的Human线程,因为run函数里面调用了Toilet类中的enter函数,这个函数是互斥的,一次只能有一个线程执行。

程序两次执行的结果如下:

Jeff enters the toilet!
Jeff has left the toilet!
Joe enters the toilet!
Joe has left the toilet!
Ann enters the toilet!
Ann has left the toilet!
Ann enters the toilet!
Ann has left the toilet!
Jeff enters the toilet!
Jeff has left the toilet!
Joe enters the toilet!
Joe has left the toilet!

可以看到,线程执行的顺序并不是固定的,但是同一时刻一定只有一个线程可以执行enter这段代码。

5.同步

主要涉及两个函数

wait():使调用该方法的线程进入休眠

notify():使调用该方法的线程被唤醒

6.线程优先级

MAX_PRIORITY  //最高优先级
NORM_PRIORITY  //普通(默认)优先级
MIN_PRIORITY  //最低优先级

如果上述main函数中通过调用MyRunnable类实现多线程的main函数中的程序改为如下:

MyRunnable mr = new MyRunnable(1);
Thread t = new Thread(mr);
t.setPriority(Thread.MIN_PRIORITY);

MyRunnable mr2 = new MyRunnable(2);
Thread t2 = new Thread(mr2);
t2.setPriority(Thread.NORM_PRIORITY);

MyRunnable mr3 = new MyRunnable(3);
Thread t3 = new Thread(mr3);
t3.setPriority(Thread.MAX_PRIORITY);

t.start();
t2.start();
t3.start();

但是我没有得到书上的结果,大部分情况下是线程3先执行,但有时候也会出现线程2先执行的情况,某次执行的结果如下:

running Thread3:0
running Thread1:0
running Thread2:0
running Thread3:1
running Thread2:1
running Thread1:1
running Thread3:2
running Thread1:2
running Thread2:2
running Thread3:3
running Thread1:3
running Thread2:3
running Thread3:4
running Thread1:4
running Thread2:4
running Thread3:5
running Thread1:5
running Thread2:5
running Thread3:6
running Thread1:6
running Thread2:6
running Thread3:7
running Thread1:7
running Thread2:7
running Thread3:8
running Thread1:8
running Thread2:8
running Thread3:9
running Thread1:9
running Thread2:9

《Java编程那点事儿》读书笔记(七)——多线程,布布扣,bubuko.com

时间: 2024-10-06 15:17:48

《Java编程那点事儿》读书笔记(七)——多线程的相关文章

&lt;java编程思想&gt;第一章读书笔记二

7.伴随多态的可互换对象 前面说了继承,我们知道继承中存在基类(父类)以及导出类(子类),不知道大家有没有遇到过这种情况?就是在向一个方法中传递一个对象作为参数时,我们往往会选择传递一个基类而不是一个子类,为什么要这么做呢?其实原因也很简单,说的高大上一点就是这样做有利于代码的健壮性和可扩展性,说的详细还是有利于代码的健壮性和可扩展性,更重要的也就是可扩展性. 还拿喝可乐的例子来说,如果你传递的参数对象是可乐,那么不管你是给我百事可乐还是可口可乐我都可以接受啊,但是如果你传递的参数仅仅是百事可乐

《Java编程那点事儿》读书笔记(四)

String 1.toString:显示对象内容时系统自动调用的方法. public class TOSTRING { public String toString(){ return "this is toString method"; } } TOSTRING t = new TOSTRING(); System.out.println(t); 2.Math Math.函数 3.String 1)char charAt(int n)  //获得字符串索引为1的字符 String a

《JAVA编程那点事儿》读书笔记(二)

方法: 1. 基本的main方法: public static void main(String[] args) 2.静态方法内部调用非静态方法:重新声明一个类,通过这个类来调用非静态方法 1 public class MAINMETHOD { 2 public static void main(String[] args) { 3 MAINMETHOD mm = new MAINMETHOD(); 4 System.out.printf("%d", mm.max(5, 6)); 5

《Java编程那点事儿》读书笔记(六)

1.抛出异常:throw 异常对象; 下面的代码是一个进制转换代码,可以转换为2进制和8进制,如果输入其他参数,则抛出异常. 1 public static String transform(int value,int radix){ 2 if(value <0 ) 3 throw new IllegalArgumentException("需要转换的数字不是自然数!"); 4 5 if(radix != 2 || radix != 8) 6 throw new IllegalA

《Java编程那点事儿》读书笔记(三)

1. static 1)静态变量:Java虚拟机为静态变量开辟单独的存储空间,所以所有的对象内部的静态变量在内存中都指向同一个地址,那么不管哪个对象改变这个成员变量,所有对象中该成员变量的值都发生变化.调用的方法一般为 类名.成员变量(常量) 2)静态方法:静态方法内部只能使用静态的成员变量,调用的方法: 类名.方法名(参数) 3)静态代码块:静态代码块在该类第一次被使用时执行一次,以后再也不执行.例如: public class StaticBlock{ static{ System.out.

《Java编程那点事儿》读书笔记(五)

System 1)arraycopy int[] a = {1.2.3.4}; int[] b = new int[5]; System.arraycopy(a,1,b,3,2); //把数组a中从下标1开始的元素复制到数组b中下标为3的位置,总共复制2个元素 2)currentTimeMillis long l = System.currentTimeMillis(); //返回当前时间与1970年01月01日0时0分0秒的差值数(毫秒) 3)gc:请求系统进行垃圾回收 4)public st

《Java编程那点事儿》读书笔记(一)

觉得自己记忆力很烂的样子,读书不做笔记就好像没读一样,所以决定以后读技术类的书籍,都要做好笔记. 1.IP地址和域名:如果把IP地址类比成身份证号的话,域名就是持证人的名字. 2.端口:规定一个 设备有216个,也就是65536个端口,每个端口对应一个唯一的程序. 0~1024之间多被操作系统占用,所以实际编程时一般采用1024以后的端口号. 3.文档注释:指可以被提取出来形成程序文档的注释格式.格式如下: /** 注释内容 */ 4.最大公约数程序 1 public class MaxComm

《java并发编程的艺术》读书笔记-第三章Java内存模型(二)

一概述 本文属于<java并发编程的艺术>读书笔记系列,第三章java内存模型第二部分. 二final的内存语义 final在Java中是一个保留的关键字,可以声明成员变量.方法.类以及本地变量.可以参照之前整理的关键字final.这里作者主要介绍final域的内存语义. 对于final域,编译器和处理器要遵守两个重排序规则: 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序. 初次读一个包含final域的对象的引用,与随后初次读这

《Java并发变成实践》读书笔记---第一章 简介

<Java并发编程实战>深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册.书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险.构造线程安全的类及验证线程安全的规则,如何将小的线程安全类组合成更大的线程安全类,如何利用线程来提高并发应用程序的吞吐量,如何识别可并行执行的任务,如何提高单线程子系统的响应性,如何确保并发程序执行预期任务,如何提高并发代码的性能和可伸缩性等内容,最后介绍了一些高级主题,如显式锁.原子变量.非阻塞算法以及