【java】java基础(二)


(3)生产者和消费者多线程体现(线程间通信问题)
以学生作为资源来实现的

资源类:Student
设置数据类:SetThread(生产者)
获取数据类:GetThread(消费者)
测试类:StudentDemo

代码:
A:最基本的版本,只有一个数据。

资源类:Student

public class Student {
    String name;
    int age;
}


设置数据类:SetThread(生产者)

public class SetThread implements Runnable {

    private Student s;

    public SetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        // Student s = new Student();
        s.name = "林青霞";
        s.age = 27;
    }
}


获取数据类:GetThread(消费者)

public class GetThread implements Runnable {
    private Student s;

    public GetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        // Student s = new Student();
        System.out.println(s.name + "---" + s.age);
    }

}


测试类:StudentDemo

/*
 * 分析:
 * 		资源类:Student
 * 		设置学生数据:SetThread(生产者)
 * 		获取学生数据:GetThread(消费者)
 * 		测试类:StudentDemo
 *
 * 问题1:按照思路写代码,发现数据每次都是:null---0
 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个
 * 如何实现呢?
 * 		在外界把这个数据创建出来,通过构造方法传递给其他的类。
 *
 */
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();
    }
}


B:改进版本,给出了不同的数据,并加入了同步机制

public class Student {
    String name;
    int age;
}
public class SetThread implements Runnable {

    private Student s;
    private int x = 0;

    public SetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (s) {
                if (x % 2 == 0) {
                    s.name = "哈哈";//刚走到这里,就被别人抢到了执行权
                    s.age = 27;
                } else {
                    s.name = "嘻嘻"; //刚走到这里,就被别人抢到了执行权
                    s.age = 30;
                }
                x++;
            }
        }
    }
}
public class GetThread implements Runnable {
    private Student s;

    public GetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (s) {
                System.out.println(s.name + "---" + s.age);
            }
        }
    }
}
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();
    }


C:等待唤醒机制改进该程序,让数据能够实现依次的出现

* 分析:
*     资源类:Student
*     设置学生数据:SetThread(生产者)
*     获取学生数据:GetThread(消费者)
*     测试类:StudentDemo
*
* 问题1:按照思路写代码,发现数据每次都是:null---0
* 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个
* 如何实现呢?
*     在外界把这个数据创建出来,通过构造方法传递给其他的类。
*
* 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题
*     A:同一个数据出现多次
*     B:姓名和年龄不匹配
* 原因:
*     A:同一个数据出现多次
*        CPU的一点点时间片的执行权,就足够你执行很多次。
*     B:姓名和年龄不匹配
*        线程运行的随机性
* 线程安全问题:
*     A:是否是多线程环境    是
*     B:是否有共享数据     是
*     C:是否有多条语句操作共享数据    是
* 解决方案:
*     加锁。
*     注意:
*        A:不同种类的线程都要加锁。
*        B:不同种类的线程加的锁必须是同一把。
*
* 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。
* 如何实现呢?
*     通过Java提供的等待唤醒机制解决。
*
* 等待唤醒:
*     Object类中提供了三个方法:
*        wait():等待
*        notify():唤醒单个线程
*        notifyAll():唤醒所有线程
*     为什么这些方法不定义在Thread类中呢?
*        这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。
*        所以,这些方法必须定义在Object类中。
*
* 最终版代码中:
*     把Student的成员变量给私有的了。
*     把设置和获取的操作给封装成了功能,并加了同步。
*     设置或者获取的线程里面只需要调用方法即可。

资源类:Student

    String name;
    int age;
    boolean flag; // 默认情况是没有数据,如果是true,说明有数据


设置数据类:SetThread(生产者)

public class SetThread implements Runnable {

    private Student s;
    private int x = 0;

    public SetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (s) {
                //判断有没有
                if(s.flag){
                    try {
                        s.wait(); //t1等着,释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                if (x % 2 == 0) {
                    s.name = "哈哈";
                    s.age = 27;
                } else {
                    s.name = "嘻嘻";
                    s.age = 30;
                }
                x++; //x=1

                //修改标记
                s.flag = true;
                //唤醒线程
                s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。
            }
            //t1有,或者t2有
        }
    }
}


获取数据类:GetThread(消费者)

public class GetThread implements Runnable {
    private Student s;

    public GetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (s) {
                if(!s.flag){
                    try {
                        s.wait(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println(s.name + "---" + s.age);
                //林青霞---27
                //刘意---30

                //修改标记
                s.flag = false;
                //唤醒线程
                s.notify(); //唤醒t1
            }
        }
    }
}


测试类: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();
    }

wait()
notify()
notifyAll() (多生产多消费)
D:等待唤醒机制的代码优化。把数据及操作都写在了资源类中(最终版)

资源类:Student

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) {
                e.printStackTrace();
            }
        }

        // 设置数据
        this.name = name;
        this.age = age;

        // 修改标记
        this.flag = true;
        this.notify();
    }

    public synchronized void get() {
        // 如果没有数据,就等待
        if (!this.flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 获取数据
        System.out.println(this.name + "---" + this.age);

        // 修改标记
        this.flag = false;
        this.notify();
    }
}

设置数据类:SetThread(生产者)

public class SetThread implements Runnable {

    private Student s;
    private int x = 0;

    public SetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            if (x % 2 == 0) {
                s.set("张三", 27);
            } else {
                s.set("李四", 30);
            }
            x++;
        }
    }
}


获取数据类:GetThread(消费者)

public class GetThread implements Runnable {
    private Student s;

    public GetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            s.get();
        }
    }
}


测试类: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)线程组

* 线程组: 把多个线程组合到一起。
* 线程类里面的方法:public final ThreadGroup getThreadGroup()
* 线程组里面的方法:public final String getName()
* ThreadGroup(String name)
* Thread(ThreadGroup group, Runnable target, String name)
* 它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
* 通过结果我们知道了:线程默认情况下属于main线程组
* 默任情况下,所有的线程都属于同一个组
        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线程组
        // 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组
        System.out.println(Thread.currentThread().getThreadGroup().getName());
        /**
         * ThreadGroup(String name)
         */
        ThreadGroup tg = new ThreadGroup("这是一个新的组");

        MyRunnable my = new MyRunnable();
        /**
         * Thread(ThreadGroup group, Runnable target, String name)
         */
        Thread t1 = new Thread(tg, my, "哈哈");
        Thread t2 = new Thread(tg, my, "嘻嘻");

        System.out.println(t1.getThreadGroup().getName());
        System.out.println(t2.getThreadGroup().getName());

        //通过组名称设置后台线程,表示该组的线程都是后台线程
        tg.setDaemon(true);

(5)线程池

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

        // 可以执行Runnable对象或者Callable对象代表的线程
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());

        //结束线程池
        pool.shutdown();
    }

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

* 多线程实现的方式3:
*      A:创建一个线程池对象,控制要创建几个线程对象。
*        public static ExecutorService newFixedThreadPool(int nThreads)
*     B:这种线程池的线程可以执行:
*        可以执行Runnable对象或者Callable对象代表的线程
*        做一个类实现Runnable接口。
*     C:调用如下方法即可
*        Future<?> submit(Runnable task)
*        <T> Future<T> submit(Callable<T> task)
*     D:我就要结束,可以吗?
*        可以。
public class MyCallable implements Callable{
    @Override
    public Object call() throws Exception {
        for (int x = 0; x < 100; x++) {
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
        return null;
    }
}
        //创建线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(2);

        //可以执行Runnable对象或者Callable对象代表的线程
        pool.submit(new MyCallable());
        pool.submit(new MyCallable());

        //结束
        pool.shutdown();

===========线程求和案例============:

public class MyCallable implements Callable<Integer> {

    private int number;

    public MyCallable(int number) {
        this.number = number;
    }

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int x = 1; x <= number; x++) {
            sum += x;
        }
        return sum;
    }

}
        // 创建线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(2);

        // 可以执行Runnable对象或者Callable对象代表的线程
        Future<Integer> f1 = pool.submit(new MyCallable(2));
        Future<Integer> f2 = pool.submit(new MyCallable(3));
        Future<Integer> f3 = pool.submit(new MyCallable(4));

        // V get()
        Integer i1 = f1.get();
        Integer i2 = f2.get();
        Integer i3 = f3.get();

        System.out.println(i1); // 3
        System.out.println(i2); // 6
        System.out.println(i3); // 10

        // 结束
        pool.shutdown();

(6)匿名内部类的格式

* 匿名内部类的格式:
*     new 类名或者接口名() {
*        重写方法;
*     };
*     本质:是该类或者接口的子类对象。
/*
 * 匿名内部类的格式:
 * 		new 类名或者接口名() {
 * 			重写方法;
 * 		};
 * 		本质:是该类或者接口的子类对象。
 */
public class ThreadDemo {
    public static void main(String[] args) {
        // 继承Thread类来实现多线程
        new Thread() {
            @Override
            public void run() {
                for (int x = 0; x < 100; x++) {
                    System.out.println(Thread.currentThread().getName() + ":" + x);
                }
            }
        }.start();

        // 实现Runnable接口来实现多线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int x = 0; x < 100; x++) {
                    System.out.println(Thread.currentThread().getName() + ":" + x);
                }
            }
        }) {
        }.start();

        // 更有难度的
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int x = 0; x < 100; x++) {
                    System.out.println("hello" + ":" + x);
                }
            }
        }) {
            @Override
            public void run() {
                for (int x = 0; x < 100; x++) {
                    System.out.println("world" + ":" + x);
                }
            }
        }.start();
    }
}

(7)定时器

* 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。
* 依赖Timer和TimerTask这两个类:
* Timer:定时
*     public Timer()
*     public void schedule(TimerTask task,long delay)
*     public void schedule(TimerTask task,long delay,long period)
*     public void cancel()
* TimerTask:任务
public class TimerDemo {
    public static void main(String[] args) {
        // 创建定时器对象
        Timer t = new Timer();
        // 3秒后执行爆炸任务
        // t.schedule(new MyTask(), 3000);
        //结束任务
        t.schedule(new MyTask(t), 3000);
    }
}

// 做一个任务
class MyTask extends TimerTask {

    private Timer t;

    public MyTask(){}

    public MyTask(Timer t){
        this.t = t;
    }

    @Override
    public void run() {
        System.out.println("bang,爆炸了");
        t.cancel();
    }
}
public class TimerDemo2 {
    public static void main(String[] args) {
        // 创建定时器对象
        Timer t = new Timer();
        // 3秒后执行爆炸任务第一次,如果不成功,每隔2秒再继续炸
        t.schedule(new MyTask2(), 3000, 2000);
    }
}

// 做一个任务
class MyTask2 extends TimerTask {
    @Override
    public void run() {
        System.out.println("bang,爆炸了");
    }
}
/*
 * 需求:在指定的时间删除我们的指定目录(你可以指定c盘,但是我不建议,我使用项目路径下的demo)
 */

import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

class DeleteFolder extends TimerTask {

    @Override
    public void run() {
        File srcFolder = new File("demo");
        deleteFolder(srcFolder);
    }

    // 递归删除目录
    public void deleteFolder(File srcFolder) {
        File[] fileArray = srcFolder.listFiles();
        if (fileArray != null) {
            for (File file : fileArray) {
                if (file.isDirectory()) {
                    deleteFolder(file);
                } else {
                    System.out.println(file.getName() + ":" + file.delete());
                }
            }
            System.out.println(srcFolder.getName() + ":" + srcFolder.delete());
        }
    }
}

public class TimerTest {
    public static void main(String[] args) throws ParseException {
        Timer t = new Timer();

        String s = "2018-07-17 14:46:00";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = sdf.parse(s);

        t.schedule(new DeleteFolder(), d);
    }
}

(8)多线程的面试题

1:多线程有几种实现方案,分别是哪几种?
    两种。
    
    继承Thread类
    实现Runnable接口
    
    扩展一种:实现Callable接口。这个得和线程池结合。

2:同步有几种方式,分别是什么?
    两种。
    
    同步代码块
    同步方法

3:启动一个线程是run()还是start()?它们的区别?
    start();
    
    run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用
    start():启动线程,并由JVM自动调用run()方法

4:sleep()和wait()方法的区别
    sleep():必须指时间;不释放锁。
    wait():可以不指定时间,也可以指定时间;释放锁。

5:为什么wait(),notify(),notifyAll()等方法都定义在Object类中
    因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。
    而Object代码任意的对象,所以,定义在这里面。

6:线程的生命周期图
    新建 -- 就绪 -- 运行 -- 死亡
    新建 -- 就绪 -- 运行 -- 阻塞 -- 就绪 -- 运行 -- 死亡
    建议:画图解释。

2:设计模式(理解)

(1)面试对象的常见设计原则
单一
开闭
里氏
依赖注入
接口
迪米特
(2)设计模式概述和分类
A:经验的总结
B:三类
创建型
结构型
行为型
(3)改进的设计模式
A:简单工厂模式
B:工厂方法模式
C:单例模式(掌握)
a:饿汉式
b:懒汉式
(4)Runtime
JDK提供的一个单例模式应用的类。
还可以调用dos命令。

public static void main(String[] args) throws IOException {
		Runtime r = Runtime.getRuntime();
//		r.exec("winmine");
		r.exec("notepad"); // 打开记事本
		r.exec("calc"); // 打开计算器
//		r.exec("shutdown -s -t 10000");
//		r.exec("shutdown -a");
	}

============================================================================

1:如何让Netbeans的东西Eclipse能访问。

在Eclipse中创建项目,把Netbeans项目的src下的东西给拿过来即可。
注意:修改项目编码为UTF-8

2:GUI(了解)

(1)用户图形界面
GUI:方便直观
CLI:需要记忆一下命令,麻烦
(2)两个包:
java.awt:和系统关联较强
javax.swing:纯Java编写
(3)GUI的继承体系
组件:组件就是对象
容器组件:是可以存储基本组件和容器组件的组件。
基本组件:是可以使用的组件,但是必须依赖容器。
(4)事件监听机制(理解)
A:事件源
B:事件
C:事件处理
D:事件监听
(5)适配器模式(理解)
A:接口
B:抽象适配器类
C:实现类
(6)案例:
A:创建窗体案例
B:窗体关闭案例
C:窗体添加按钮并对按钮添加事件案例。
界面中的组件布局。
D:把文本框里面的数据转移到文本域
E:更改背景色
F:设置文本框里面不能输入非数字字符
G:一级菜单
H:多级菜单
(7)Netbeans的概述和使用
A:是可以做Java开发的另一个IDE工具。
B:使用
A:四则运算
a:修改图标
b:设置皮肤
c:设置居中
d:数据校验
B:登录注册

============================================================================

1:网络编程(理解)

(1)网络编程:用Java语言实现计算机间数据的信息传递和资源共享
(2)网络编程模型
(3)网络编程的三要素
A:IP地址
a:点分十进制
b:IP地址的组成
c:IP地址的分类
d:dos命令
e:InetAddress
B:端口
是应用程序的标识。范围:0-65535。其中0-1024不建议使用。
C:协议
UDP:数据打包,有限制,不连接,效率高,不可靠
TCP:建立数据通道,无限制,效率低,可靠
(3)Socket机制
A:通信两端都应该有Socket对象
B:所有的通信都是通过Socket间的IO进行操作的
(4)UDP协议发送和接收数据(掌握 自己补齐代码)
发送:
创建UDP发送端的Socket对象
创建数据并把数据打包
发送数据
释放资源

接收:
创建UDP接收端的Socket对象
创建数据包用于接收数据
接收数据
解析数据包
释放资源
(5)TCP协议发送和接收数据(掌握 自己补齐代码)
发送:
创建TCP客户端的Socket对象
获取输出流,写数据
释放资源

接收:
创建TCP服务器端的Socket对象
监听客户端连接
获取输入流,读取数据
释放资源
(6)案例:
A:UDP
a:最基本的UDP协议发送和接收数据
b:把发送数据改进为键盘录入
c:一个简易聊天小程序并用多线程改进
B:TCP
a:最基本的TCP协议发送和接收数据
b:服务器给出反馈
c:客户端键盘录入服务器控制台输出
d:客户端键盘录入服务器写到文本文件
e:客户端读取文本文件服务器控制台输出
f:客户端读取文本文件服务器写到文本文件
g:上传图片
h:多线程改进上传文件

============================================================================

1:反射(理解)

(1)类的加载及类加载器
(2)反射:
通过字节码文件对象,去使用成员变量,构造方法,成员方法
(3)反射的使用
A:通过反射获取构造方法并使用
B:通过反射获取成员变量并使用
C:通过反射获取成员方法并使用
(4)反射案例
A:通过反射运行配置文件的内容
B:通过反射越过泛型检查
C:通过反射给任意的一个对象的任意的属性赋值为指定的值
(5)动态代理

2:设计模式

(1)装饰设计模式
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

Scanner sc = new Scanner(System.in);
(2)模版设计模式

3:JDK新特性

(1)JDK5(掌握)
装箱和拆箱
泛型
增强for
静态导入
可变参数
枚举
(2)JDK6(了解)
(3)JDK7(理解)
二进制的表现形式
用_分隔数据
switch语句可是用字符串
泛型推断(菱形泛型)
多catch的使用
自动释放资源的用法
(4)JDK8(了解)
可以去网上了解资料

猜你喜欢

转载自blog.csdn.net/shiki_41/article/details/81076411
今日推荐