Java中Thread线程总结(下)

来源:互联网 发布:mac 删除云梯 编辑:程序博客网 时间:2024/04/28 01:42

1多线程(理解)

(1)JDK5以后的针对线程的锁定操作和释放操作

        Lock锁

(2)死锁问题的描述和代码体现

package day24_Thread;public class DieLock extends Thread {    //定义两把锁    private static final Object objA=new Object();    private static final Object objB=new Object();    //标志位    private boolean flag;    //通过传递构造参数设置标志位    public DieLock(boolean flag) {        // TODO Auto-generated constructor stub        this.flag=flag;    }    //重写run方法    @Override    public void run() {        // TODO Auto-generated method stub        if(flag){            synchronized (objA) {                System.out.println("if objA");                synchronized (objB) {                    System.out.println("if objB");                }            }        }else{            synchronized (objB) {                System.out.println("else objB");                synchronized (objA) {                    System.out.println("else objA");                }            }        }    }}package day24_Thread;/* * 同步的效率: *      A:效率低 *      B:容易产生死锁 *  * 死锁: *      两个或两个以上的线程在争夺资源的过程中,发生一种相互等待的现象 *  * 举例: *      中国人,美国人吃饭 *      正常情况: *          中国人:筷子两只 *          美国人:刀和叉 *       *      现在: *          中国人:筷子一只,刀一把 *          美国人:筷子一只,叉一把 * */public class DieLockDemo {    public static void main(String[] args) {        // TODO Auto-generated method stub        //创建对象        DieLock dl1 = new DieLock(true);        DieLock dl2 = new DieLock(false);        //启动线程        dl1.start();        dl2.start();    }}

(3)生产者和消费者多线程体现(线程间通信问题)

        以学生作为资源来实现        资源类:Student        设置数据类:SetThread()生产者        获取数据类:GetThread()消费者        测试类:StudentDemo        代码:            A:最基本的版本,只有一个数据            B:改进版本,给出了不同的数据,并加入了同步机制            C:等待唤醒机制改进该程序,让数据能够实现依次的出现                wait()                notify()                notifyAll()多生产多消费            D:等待唤醒机制的代码优化,把数据及操作都写在了资源类中。

这里写图片描述

package day24_Thread_Pro_Customer2;public class Student {    //设为私有,其他成员不能直接访问    private String name;    private int age;    private boolean flag;//默认是没有数据,如果是true,说明有数据    //设置方法    public synchronized void set(String name,int age){        if(this.flag){            //有数据就等待            try {                this.wait();            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }        //没有数据就设置数据        this.name=name;        this.age=age;        //将标志位置为true        this.flag=true;        //唤醒        this.notify();    }    //获取方法    public synchronized void get(){        if(!this.flag){            //没有数据就等待            try {                this.wait();            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }           //有数据就取数据        System.out.println(this.name+"---"+this.age);        //标志位为false        this.flag=false;        //唤醒        this.notify();    }}package day24_Thread_Pro_Customer2;public class SetThread implements Runnable {    private Student s;    private int x=0;    public SetThread(Student s) {        // TODO Auto-generated constructor stub        this.s=s;    }    @Override    public void run() {        // TODO Auto-generated method stub        while(true){            //同步代码块            synchronized (s) {                //没有则继续产生,执行后面代码                if(x%2==0){                    s.set("林青霞", 27);                }else{                    s.set("刘毅", 30);                }                x++;            }        }    }}package day24_Thread_Pro_Customer2;public class GetThread implements Runnable {    private Student s;    public GetThread(Student s) {        // TODO Auto-generated constructor stub        this.s=s;    }    @Override    public void run() {        // TODO Auto-generated method stub        while(true){            synchronized (s) {                s.get();            }               }    }}package day24_Thread_Pro_Customer2;/* * 分析: *      资源类:Student *      设置学生数据:SetThread(生产者) *      获取学生数据:GetThread(消费者) *      测试类:StudentDemo *  * 问题1:按照思路写代码,发现数据每次都是:null -- 0 *      原因:我们在每个数据中创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个。 *  *  如何实现? *      在外界把这个数据创建出来,通过构造方法传递给其他的类。(Student s) *  * 问题2:为了数据的效果更好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题 *      A:同一个数据出现多次 *      B:姓名和年龄不匹配 *  原因: *      A:同一个数据出现多次 *          CPU的一点点时间片的执行权,就足够你执行很多次 *      B:姓名和年龄不匹配 *          线程运行的随机性 * 线程安全问题: *      A:是否是多线程环境  是 *      B:是否有共享数据   是 *      C:是否有多条语句操作共享数据 是 * 解决方案: *      加锁 *      注意: *          A:不同种类的线程都要加锁 *          B:不同的线程加的锁必须是同一把锁   *  * 问题3:虽然数据安全了,但是呢,一次一大片不好看,我想依次的一次一个输出 * 如何实现呢? *      通过Java提供的等待唤醒机制解决 *  * 等待唤醒: *      Object类提供了三个方法: *          wait():等待 *          notify():唤醒单个线程 *          notifyAll():唤醒所有线程 *      为什么这些方法不定义在Thread类中呢? *           这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是在任意锁对象。 *          所以,这些方法必须定义在Object类中。 *  * 最终版代码: *      把Student的成员变量给私有的了。 *      把设置和获取的操作给封装成了功能,并加了同步。 *      设置或者获取的线程里面值需要调用方法即可。 * */public class StudentDemo {    public static void main(String[] args) {        //创建资源        Student s = new Student();        //设置获取类和获取类        SetThread st = new SetThread(s);        GetThread gt = new GetThread(s);        //线程类        Thread t1 = new Thread(st);        Thread t2 = new Thread(gt);        //启动线程        t1.start();        t2.start();    }}

(4)线程组

package day24_ThreadGroup;public class MyRunnable implements Runnable {    @Override    public void run() {        // TODO Auto-generated method stub        for(int i=0;i<100;i++){            System.out.println(Thread.currentThread().getName()+":"+i);        }    }}package day24_ThreadGroup;/* * 线程组:把多个线程组合到一起 * 它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制 * */public class ThreadGroupDemo {    public static void main(String[] args) {        // TODO Auto-generated method stub        method1();        System.out.println(Thread.currentThread().getThreadGroup().getName());        //我们何如修改线程所在的组呢?        //创建一个线程组        //创建其他线程的时候,把其他线程的组指定为我们自己新建的线程组        method2();    }    private static void method2() {        // TODO Auto-generated method stub        ThreadGroup tg = new ThreadGroup("这是一个新的线程组");        MyRunnable my = new MyRunnable();        Thread t1 = new Thread(tg,my,"林青霞");        Thread t2 = new Thread(tg,my, "刘意");        System.out.println(t1.getThreadGroup().getName());        System.out.println(t2.getThreadGroup().getName());        //利用线程组统一修改该组数据        //通过组名称设置后台线程,表示该组的线程都是后台线程。        t1.setDaemon(true);    }    public static void method1(){        MyRunnable my = new MyRunnable();        //创建线程        Thread t1 = new Thread(my, "林青霞");        Thread t2 = new Thread(my, "刘意");        //我不知道他们属于哪个线程组,怎么办?        //线程组里方法:public final ThreadGroup getThreadGroup()        ThreadGroup tg1 = t1.getThreadGroup();        ThreadGroup tg2 = t2.getThreadGroup();        //线程组的方法:public final String getName()        String name1=tg1.getName();        String name2=tg2.getName();        System.out.println(name1);        System.out.println(name2);        //通过结果我们知道了,线程默认情况下属于main线程组    }}

(5)线程池

package day24_ThreadPool;public class MyRunnable implements Runnable {    @Override    public void run() {        // TODO Auto-generated method stub        for(int i=0;i<100;i++){            System.out.println(Thread.currentThread().getName()+":"+i);        }    }}package day24_ThreadPool;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/* * 线程池的好处:线程池的每一个线程代码结束后,都不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用 *  * 如何实现线程的代码呢? *      A:创建一个线程池的对象,控制要创建几个线程对象 *          public static ExecutorService newFixedThreadPool(int nThreads) *      B:这种线程池的线程可以执行 *          可以执行Runnable对象或者Callable对象代表的线程 *          做一个类实现Runnable接口 *      C:调用如下方法即可 *          Future<?>submit (Runnable task) *          <T> Future<T> submit(Callable<T> task) *       * */public class ExecutorsDemo {    public static void main(String[] args) {        // TODO Auto-generated method stub        //创建一个线程池对象,控制要创建几个线程对象        //public static ExecutorService newFixedThreadPool(int nThreads)        ExecutorService pool = Executors.newFixedThreadPool(2);        //可以执行Runable对象或者Callable对象代表的线程        pool.submit(new MyRunnable());        pool.submit(new MyRunnable());        //结束线程池        pool.shutdown();    }}

(6)多线程实现的第三种方案

package day24_Callable;import java.util.concurrent.Callable;/* * 多线程求和 * */public class MyCallable implements Callable<Integer> {    private int number;    //通过构造方法传递number    public MyCallable(int number) {        // TODO Auto-generated constructor stub        this.number=number;    }    @Override    public Integer call() throws Exception {        // TODO Auto-generated method stub        int sum=0;        for(int i=1;i<=number;i++){            sum+=i;        }        return sum;    }}package day24_Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;/* * 线程池的好处:线程池的每一个线程代码结束后,都不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用 *  * 如何实现线程的代码呢? *      A:创建一个线程池的对象,控制要创建几个线程对象 *          public static ExecutorService newFixedThreadPool(int nThreads) *      B:这种线程池的线程可以执行 *          可以执行Runnable对象或者Callable对象代表的线程 *          做一个类实现Runnable接口 *      C:调用如下方法即可 *          Future<?>submit (Runnable task) *          <T> Future<T> submit(Callable<T> task) * */public class MyCallableDemo {    public static void main(String[] args) throws InterruptedException, ExecutionException {        // TODO Auto-generated method stub        //创建线程池对象        ExecutorService pool = Executors.newFixedThreadPool(2);        //可以执行Runnable对象或者Callable对象代表的线程        Future<Integer> f1 = pool.submit(new MyCallable(100));        Future<Integer> f2 = pool.submit(new MyCallable(200));        Integer i1=f1.get();        Integer i2 = f2.get();        System.out.println("i1:"+i1);        System.out.println("i2:"+i2);        //结束        pool.shutdown();    }}

(7)多线程的面试题

1:多线程有几种实现方案,分别是哪几种?    一般答两种:        继承Thread类        实现Runnable接口    拓展一种:实现Callable接口,这个得和线程池结合2:同步有几种方式,分别是什么?    两种        同步代码块        同步方法3:启动一个线程是run()还是start()?它们的区别是?        start()        run():封装了被线程执行的代码,直接调用仅仅是普通的调用        statr():启动线程,并有jvm自动调用run()方法4:sleep()和wait()方法的区别是?        sleep():必须指时间;不释放锁        wait():它可以不指定时间,也可以指定时间;释放锁5:为什么wait()、notify()、notifyAll()等方法都是定义在Object类中?        因为这些方法调用时依赖于锁对象的,而同步代码块的锁对象是任意锁        而object代表任意的对象,所以,定义在这里面。6:线程的生命周期图?        新建--就绪--运行--死亡        新建--就绪--运行--阻塞--就绪--运行--死亡        建议:画图解释

这里写图片描述

原创粉丝点击