整理笔记--Java基础(多线程)

JAVA学习网 2018-03-15 10:38:02

java运行时最起码有两条线程,主线程和垃圾回收线程。

并行:指物理上同时执行。

并发:指能够让多个任务在逻辑上交织执行的程序设计。

进程:正在运行的程序。

线程:进程的一条执行路径。

随机性原理:因为cpu的快速切换造成的,线程会争夺cpu的执行权。

 

线程的运行代码统一存放在了run方法中,用start方法开启线程。

直接调用run()方法的话,系统只会把线程对象当成一个普通对象,把run()方法也当成一个普通方法,而不是线程执行体。

创建线程的方式:

  1.继承Thread类

public class Test01 {
    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 4,创建Thread类的子类对象
        mt.start(); // 5,开启线程
        for (int i = 0; i < 1000; i++) {
            System.out.println("bb");
        }
    }
}

class MyThread extends Thread { // 1,继承Thread
    public void run() { // 2,重写run方法
        for (int i = 0; i < 1000; i++) { // 3,将要执行的代码写在run方法中
            System.out.println("第一种方式");
        }
    }
}

返回当前线程的名称:this.getName()

好处:可以直接使用Thread类中的方法。

弊端:有父类就不能用这种方法(即单继承的弊端)。

  

  2.实现Runnable接口

public class Test02 {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable(); // 4,创建Runnable的子类对象
        Thread t = new Thread(mr); // 5,将其当作参数传递给Thread的构造函数
        t.start(); // 6,开启线程
        for (int i = 0; i < 1000; i++) {
            System.out.println("bb");
        }
    }
}

class MyRunnable implements Runnable { // 1,定义一个类实现Runnable
    @Override
    public void run() { // 2,重写run方法
        for (int i = 0; i < 1000; i++) { // 3,将要执行的代码写在run方法中
            System.out.println("第二种方式");
        }
    }
}

返回当前线程的名称:Thread.currentThread().getName()

好处和弊端刚好和继承Thread类相反,两者是互补的。

  

  3.匿名内部类方式

public class Test03 {
    public static void main(String[] args) {
        new Thread() {
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println("AA");
                }
            };
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println("BB");
                }
            }
        }).start();
    }
}

 

控制线程:

  1.设置线程的名字:setName()通过构造方法
  2.获取名字getName()   3.获取当前线程的对象Thread.currentThread();   4.让线程休眠指定的时间Thread.sleep(毫秒值)   5.守护线程setDaemon(true): 把一个线程设置为守护线程(当其他线程都结束了,它如果没有执行完毕也会结束.)   6.让线程插队:join(): 让指定的线程优先执行,然后其他的线程再执行   7.礼让线程:yield(): 让出CPU执行权.   8.设置线程的优先级:线程优先级从: 1-10   默认的线程优先级: 5 setPriority()

 

线程安全问题(常用同步代码块):

由于多个线程在操作共享数据,且多条语句对共享数据进行运算,所以产生了多线程安全问题,从而有了同步代码块。

同步的前提:

  1.至少两个线程。

  2.必须保证是使用同一个锁。

补充:多次start()同一个线程是非法的。

 

同步代码块和同步函数的特点?

  1.同步代码块使用的锁可以是任意对象(但不能用匿名对象)。

  2.同步函数(在方法上加synchronized修饰就行)使用的锁是this,静态同步函数的锁是该类的字节码文件对象。

在一个类中只有一个同步,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。

 

死锁:就是同步方法中有同步代码块,或反之。

 

线程间的通信(常用同步函数):

思路:多个线程在操作同一个资源,但是操作的动作却不一样。

  1.将资源封装成对象。  

  2.将线程执行的任务(任务其实就是run方法)也封装成对象。

 

等待唤醒机制:

wait:将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线程池中。
notify:唤醒线程池中某一个等待线程。
notifyAll:唤醒的是线程池中的所有线程。

 

wait和sleep区别:

  wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者notifyAll来唤醒。
  sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。
  wait:线程会释放执行权,而且线程会释放锁。
  sleep:线程会释放执行权,但是不释放锁。

 

同步锁synchronized和互斥锁 ReentrantLock 的区别:

  1.同步是隐示的锁操作,而Lock对象是显示的锁操作。

  2.互斥锁 ReentrantLock—java5 三个或者以上通信(推荐使用)。

例子:

ReentrantLock lck = new ReentrantLock();//创建一个互斥锁对象lck.lock(): 获取锁对象
//被同步的内容
lck.unlock();//释放锁对象
//监视器
Condition c = lck.newCondition();//获取监视器对象
c.signal();//唤醒等待中的线程 signalAll()唤醒所有
c.await();//让线程等待.

 

线程的状态:

  

线程的停止:让线程运行的代码结束,也就是结束run方法。

怎么结束run方法?一般run方法里肯定定义循环。所以只要结束循环即可。

  1.定义循环的结束标记。

  2.如果线程处于了冻结状态,是不可能读到标记的。这时就需要通过Thread类中的interrupt方法,将其冻结状态强制清除。让线程恢复具备执行资格的状态,让线程可以读到标记,并结束。

 

线程池:

  A:线程池概述:

  程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互,而使用线程池可以很好的提高性能。

  例如:常量池中会存储-128~127之间的Integer对象原理一样

  例子(略)

 

在静态方法上使用同步时会发生什么事?

  同步静态方法时会获取该类的“Class”对象,所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。

 

当一个同步方法已经执行,线程能够调用对象上的非同步实例方法吗?

可以,一个非同步方法总是可以被调用而不会有任何问题。实际上,Java没有为非同步方法做任何检查,锁对象仅仅在同步方法或者同步代码块中检查。

如果一个方法没有声明为同步,即使你在使用共享数据Java照样会调用,而不会做检查是否安全,所以在这种情况下要特别小心。

一个方法是否声明为同步取决于临界区访问,如果方法不访问临界区(共享资源或者数据结构)就没必要声明为同步的。

 

用户线程和守护线程有什么区别?

所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。

   用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开,如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。

 

Java创建线程之后,直接调用start()方法和run()的区别:

  1.run()方法,在本线程内调用该Runnable对象的run()方法,可以重复多次调用。

  2.start()方法,启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程。

当你调用start()方法时你将创建新的线程,并且执行在run()方法里的代码。但是如果你直接调用run()方法,它不会创建新的线程也不会执行调用线程的代码。

 

例子1:

/**
*创建一个Bank银行类,属性有money(初始值为100),生成set/get方法,创建一个Bank银行对象,
*创建三个线程(分别给线程命名为"用户A","用户B","用户C"),
*当用户A线程执行时,通过set方法将money的值增加100,在控制台输出 "用户A线程正在执行第X次,增加了100元,目前money的值为X元"
*当用户B线程执行时,通过set方法将money值随机增加1-100(不含100),在控制台输出"用户B线程正在执行第X次,增加了X元,目前money的值为X元"
*当用户C线程执行时,线程休眠10毫秒,不作任何操作.在控制台输出"用户C线程正在执行第X次,睡眠了10毫秒"
*共执行20次,最后打印输出money的值,如 "增加后的money值为:X元"
*注意:(机子速度太快有可能会出现一条线程全执行完,关键要实现需求)
*/
public class Test01 {
    public static void main(String[] args) {
        BankThread b1 = new BankThread("用户A");
        BankThread b2 = new BankThread("用户B");
        BankThread b3 = new BankThread("用户C");
        b2.start();
        b3.start();
        b1.start();

    }
}

class BankThread extends Thread {
    BankThread(String str) {
        super(str);
    }
    static Bank b = new Bank();
    static int count = 20;
    public void run() {
        while (true) {
            synchronized (BankThread.class) {
                if (count > 20) {
                    break;
                }
                if ("用户A".equals(this.getName())) {
                    b.setMoney(b.getMoney() + 100);
                    System.out.println("用户A线程正在执行第" + count + "次,增加了100元,目前money的值为" + b.getMoney() + "元");
                } else if ("用户B".equals(this.getName())) {
                    Random r = new Random();
                    int num = r.nextInt(100);
                    b.setMoney(b.getMoney() + num);
                    System.out.println("用户B线程正在执行第" + count + "次,增加了" + num + "元,目前money的值为" + b.getMoney() + "元");
                } else if ("用户C".equals(this.getName())) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("用户C线程正在执行第X次,睡眠了10毫秒");
                }
                count++;
            }
        }
    };
}

class Bank {
    private int money = 100;
    public int getMoney() {
        return money;
    }
    public void setMoney(int money) {
        this.money = money;
    }
}

例子2:

/**
* 模拟3个老师同时发80份笔记, 每个老师相当于一个线程, 分别给三个线程命名为”张老师线程,林老师线程,李老师线程”,
* 要求在控制台输出"xxx老师"在发第"xxx"份笔记 2) 如果要求最后一张试卷必须由”李老师线程”发出,请问应该怎么做?
*/
public class Test02 {
    public static void main(String[] args) {
        ShiJuan shi = new ShiJuan();
        Thread t1 = new Thread(shi);
        Thread t2 = new Thread(shi);
        Thread t3 = new Thread(shi);
        t1.setName("张老师");
        t2.setName("林老师");
        t3.setName("李老师");
        t1.start();
        t2.start();
        t3.start();
    }
}

class ShiJuan implements Runnable {
    int shijuan = 80;
    @Override
    public void run() {
        while (true) {
            synchronized (ShiJuan.class) {
                if (shijuan <= 0) {
                    break;
                }
                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (shijuan == 1 && !Thread.currentThread().getName().equals("李老师")) {
                    continue;
                }
                System.out.println(Thread.currentThread().getName() + "正在发" + shijuan);
                System.out.println(shijuan--);
            }
        }
    }
}

 例子3:

/* 模拟抢红包过程,生成50个红包(金额是随机的,范围在1-10元之间)
        创建5个线程代表5个人,然后让这5个人去抢这50个红包,每次抢红包需要300ms的时间,
        在控制台打印出(xxx抢了xxx元)(不限定每人抢的次数并且抢到红包后还可以接着抢,每次生成一个红包).
*/
public class Test03 {
    public static void main(String[] args) {
        HongBao_1 nb =  new HongBao_1();
        Thread t1 = new Thread(nb);
        Thread t2 = new Thread(nb);
        Thread t3 = new Thread(nb);
        Thread t4 = new Thread(nb);
        Thread t5 = new Thread(nb);
        t1.setName("线程1");
        t2.setName("线程2");
        t3.setName("线程3");
        t4.setName("线程4");
        t5.setName("线程5");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        }}

        class HongBao_1 implements Runnable{
        //这里的变量不需要静态
        int hongbao=50;
        Random r = new Random();
        @Override
        public void run() {
        while(true){
        synchronized(HongBao_1.class){
        if(hongbao<=0){
        break;
        }
        try {
        Thread.sleep(30);
        } catch (InterruptedException e) {
        e.printStackTrace();
        }
        int num = r.nextInt(10)+1;
        System.out.println(Thread.currentThread().getName()+"抢到了"+num+"元");
        System.out.println(hongbao--);
        }}
    }
}

例子4:

/**
* 三个老师,发80份卷子,要求每个老师轮流发.
*/
public class Test04 {
    public static void main(String[] args) {
        final Paper p = new Paper();
        new Thread("张老师") {
            public void run() {
                try {
                    p.send1();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }.start();
        
        new Thread("龚老师") {
            public void run() {
                try {
                    p.send2();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }.start();
        
        new Thread("刘老师") {
            public void run() {
                try {
                    p.send3();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }
}

class Paper {
    private int paper = 80;
    private int flag = 1;
    public void send1() throws InterruptedException {
        while (true) {
            synchronized (this) {
                while (flag != 1) {
                    this.wait();
                }
                if (paper == 0) {
                    flag = 2;
                    this.notifyAll();
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "发了第"
                        + paper-- + "份卷子");
                flag = 2;
                this.notifyAll();
            }
        }
    }

    public void send2() throws InterruptedException {
        while (true) {
            synchronized (this) {
                while (flag != 2) {
                    this.wait();
                }
                if (paper == 0) {
                    flag = 3;
                    this.notifyAll();
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "发了第"
                        + paper-- + "份卷子");
                flag = 3;
                this.notifyAll();
            }
        }
    }

    public void send3() throws InterruptedException {
        while (true) {
            synchronized (this) {
                while (flag != 3) {
                    this.wait();
                }
                if (paper == 0) {
                    flag = 1;
                    this.notifyAll();
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "发了第"
                        + paper-- + "份卷子");
                flag = 1;
                this.notifyAll();
            }
        }
    }
}

例子5:

/**总和 = 从1加到10的和  +  从11加到20的和
*编写10个线程, 第一个线程从1加到10, 第二个线程从11加到20, ...,
*第十个线程从91加到100, 最后再把10个线程的结果相加输出到控制台上
*/
public class Test05 {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            XiangJia xj = new XiangJia();
            xj.setName("线程" + (i + 1));
            xj.setInit(i * 10);
            xj.start();
        }
        Thread.sleep(10);
    }
}

class XiangJia extends Thread {
    static int total;
    int inti;
    public void setInit(int inti) {
        this.inti = inti;
    }
    @Override
    public void run() {
        synchronized (XiangJia.class) {
            int sum = 0;
            for (int i = 1; i <= 10; i++) {
                sum += inti + i;
            }
            total += sum;
            System.out.println(getName() + "...total=" + total);
        }
    }
}

例子6:

/**
* 某包子店铺生意火爆,现开5个窗口模拟售卖100个包子,每次每个窗口随机卖出1-5个包子,
        卖完最后一个包子后提示”包子已售完”(必须全部卖出),程序结束.
*/
public class Test06 {
    public static void main(String[] args) {
        Baozi bz1 = new Baozi("窗口1");
        Baozi bz2 = new Baozi("窗口2");
        Baozi bz3 = new Baozi("窗口3");
        Baozi bz4 = new Baozi("窗口4");
        Baozi bz5 = new Baozi("窗口5");
        bz1.start();
        bz2.start();
        bz3.start();
        bz4.start();
        bz5.start();
    }
}

class Baozi extends Thread {
    Baozi(String str) {
        super(str);
    }

    // 包子总数
    static int baozi = 100;
    Random r = new Random();
    @Override
    public void run() {
        while (true) {
            synchronized (Baozi.class) {
                if (baozi <= 0) {
                    break;
                }
                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int nextInt = r.nextInt(5) + 1;
                if (baozi <= nextInt) {// 此时包子已经卖到不足5个
                    nextInt = baozi;
                    System.out.println(this.getName() + "卖了" + nextInt + "个");
                    System.out.println("包子已经卖完!!!!");
                } else {
                    // 此时包子是大于5个的
                    System.out.println(this.getName() + "卖了" + nextInt + "个");
                }
                baozi -= nextInt;
                System.out.println(baozi);
            }
        }
    }
}

 

阅读(796) 评论(0)