Java中Thread线程总结(下)

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:线程的生命周期图?
        新建--就绪--运行--死亡
        新建--就绪--运行--阻塞--就绪--运行--死亡
        建议:画图解释

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值