package com.it.demo01_thread;
/*
    案例: 多线程简介.
    概述:
        指的是进程有多条执行路径, 统称叫: 多线程.
        进程: 指的是可执行程序, 文件(例如: .exe)
            大白话翻译: 车.
        线程: 指的就是 进程的 执行路径(或者叫: 执行单元)
            大白话翻译: 车道.
    面试题: 多线程并行和多线程并发的区别是什么?
        多线程并行:
            指的是两个(或以上)的线程同时执行.  前提: 需要多核CPU.
        多线程并发:
             指的是两个(或以上)的线程请求执行, 但是同一瞬间CPU只能执行一个线程,
             于是就安排它们交替执行, 因为时间间隔非常短, 我们看起来好像是同时执行的, 其实不是.
    案例: 演示单线程程序, 即: 前边的代码没有执行完毕前, 后边的代码不会执行.
 */
public class Demo01 {
    public static void main(String[] args) {
        for (int i = 0; i < 200; i++) {
            System.out.println("run,....... " + i);
        }
        for (int i = 0; i < 200; i++) {
            System.out.println("main... " + i);
        }
    }
}
 
package com.it.demo01_thread;
/*
    案例: 演示多线程的实现方式一: 继承Thread类.
    在Java程序中, 线程的顶级类是: Thread, 即: 所有的线程类都是Thread类的子类.
    多线程的方式如下:
        方式一: 继承Thread类.
            步骤:
                1. 自定义一个线程类(MyThread), 让它继承Thread类.
                2. 重写Thread#run()
                3. 把要执行的代码放到 run()方法中.
                4. 在main方法中创建线程类对象.
                5. 开启线程.
        方式二: 实现Runnable接口.
        方式三: 实现Callable接口, 必须和线程池相结合使用.
    细节:
        1. 开启线程调用的是 Thread#start()方法, 如果调用run()方法, 只是普通的方法调用而已.
        2. 同一线程不能重复开启, 否则会报: IllegalThreadStateException异常.
        3. 多线程的执行具有 随机性 和 延迟性.
        4. 一台电脑上可以有多个进程, 这些进程之间的数据是相互隔离的.
        5. 一个进程可以有多条线程, 这些线程可以共享该进程的数据.
 */
public class Demo02 {
    public static void main(String[] args) {
        //4. 在main方法中创建线程类对象.
        MyThread mt = new MyThread();
        //5. 开启线程
        //mt.run();     //只是普通的方法调用.
        mt.start();
        //mt.start();   //同一线程不能重复开启
        //main线程的内容, 输出200次 main...
        for (int i = 0; i < 200; i++) {
            System.out.println("main... " + i);
        }
    }
}
 
package com.it.demo01_thread;
/*
    案例: 演示多线程的实现方式二: 实现Runnable接口.
    多线程的方式如下:
        方式一: 继承Thread类.
        方式二: 实现Runnable接口.
             步骤:
                1. 自定义一个资源类(MyRunnable), 让它实现Runnable接口.
                2. 重写Runnable#run()
                3. 把要执行的代码放到 run()方法中.
                4. 在main方法中创建资源类对象, 然后将其作为参数传入Thread类的构造, 从而创建线程对象.
                5. 开启线程.
        方式三: 实现Callable接口.
    细节:
        1. 开启线程调用的是 Thread#start()方法, 如果调用run()方法, 只是普通的方法调用而已.
        2. 同一线程不能重复开启, 否则会报: IllegalThreadStateException异常.
        3. 多线程的执行具有 随机性 和 延迟性.
        4. 一台电脑上可以有多个进程, 这些进程之间的数据是相互隔离的.
        5. 一个进程可以有多条线程, 这些线程可以共享该进程的数据.
 */
public class Demo03 {
    public static void main(String[] args) {
        //4. 在main方法中创建资源类对象, 然后将其作为参数传入Thread类的构造, 从而创建线程对象.
        //4.1 创建资源类对象
        MyRunnable mr = new MyRunnable();
        //4.2 将其作为参数传入Thread类的构造, 从而创建线程对象.
        Thread th = new Thread(mr);
        //5. 开启线程.
        th.start();
        for (int i = 0; i < 200; i++) {
            System.out.println("main... " + i);
        }
    }
}
 
package com.it.demo01_thread;
/*
    案例: 演示Thread类的构造方法和成员方法
    涉及到的Thread类中的成员:
        构造方法:
            public Thread():                    分配一个新的线程对象。
            public Thread(String name)          分配一个指定名字的新的线程对象。
            public Thread(Runnable target)      分配一个带有指定目标新的线程对象。
            public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。
        常用方法:
            public String getName():                获取当前线程名称。
            public void start():                    导致此线程开始执行; Java虚拟机调用此线程的run方法。
            public void run():                      此线程要执行的任务在此处定义代码。
            public static void sleep(long millis):  使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
            public static Thread currentThread():   返回对当前正在执行的线程对象的引用。
 */
public class Demo04 {
    public static void main(String[] args) {
        //需求: 创建两个线程对象, 并开启.
        //方式一: 继承Thread类.
        //method01();
        //方式二: 实现Runnable接口.
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr);
        t1.setName("乔峰");
        Thread t2 = new Thread(mr, "虚竹");
        t1.start();
        t2.start();
    }
    public static void method01() {
        MyThread mt1 = new MyThread();
        mt1.setName("刘亦菲");
        MyThread mt2 = new MyThread("赵丽颖");
        mt1.start();
        mt2.start();
    }
}
 
package com.it.demo01_thread;
/*
    案例: 通过匿名内部类的方式实现多线程.
 */
public class Demo05 {
    public static void main(String[] args) {
        //方式一: 继承Thread类.
        new Thread() {
            //重写run方法, 把要执行的写到方法中
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("匿名内部类,  继承Thread类版 " + i);
                }
            }
        }.start();
        //方式二: 实现Runnable接口.
        //格式: new Thread(Runnable接口的子类对象).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("匿名内部类,  实现Runnable接口.... " + i);
                }
            }
        }).start();
        //扩展: 因为Runnable接口中只有一个抽象方法, 所以可以尝试通过Lambda表达式实现.
        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println("Lambda表达式,  实现Runnable接口 " + i);
            }
        }).start();
    }
}
 
package com.it.demo01_thread;
//1. 自定义一个线程类(MyThread), 让它继承Thread类.
public class MyThread extends Thread{
    //细节: 如果要给线程起名字, 记得写构造方法
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }
    //2. 重写Thread#run()
    @Override
    public void run() {
        //3. 把要执行的代码放到 run()方法中.
        for (int i = 0; i < 200; i++) {
            System.out.println(this.getName() + "  run,....... " + i);
        };
    }
}
 
package com.it.demo01_thread;
//资源类
//1. 自定义一个资源类(MyRunnable), 让它实现Runnable接口.
public class MyRunnable implements Runnable{
    //2. 重写Runnable#run()
    @Override
    public void run() {
        //3. 把要执行的代码放到 run()方法中.
        for (int i = 0; i < 200; i++) {
            System.out.println(Thread.currentThread().getName() + "  run,....... " + i);
        }
    }
}
 
package com.it.demo02_tickets;
//自定义的线程类, 用来模拟卖票
public class MyThread extends Thread{
    //1. 定义变量, 记录票数.
    private static int tickets = 66;       //细节1: 4个窗口共享100张票
    //2. 定义构造方法.
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }
    //3. 重写run()方法, 里边写是具体的卖票的逻辑.
    @Override
    public void run() {
        //具体的卖票的逻辑.
        while(true) {
            //做越界处理, 没票就不卖了.
            if (tickets <= 0) {
                break;
            }
            //加入休眠线程, 让程序出现非法值的概率大一些.
            try {
                Thread.sleep(50);       //单位是毫秒         线程1, 线程2, 线程3, 线程4
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //具体的卖票
            System.out.println(getName() + " 正在售出第 "+ tickets-- +" 张票");
        }
        /*
            多线程模拟卖票, 会出现非法值的问题:
                出现负数:
                    核心点: 1. if判断  2. sleep(), 在哪睡, 就在哪醒.
                    具体流程:
                        1. 当tickets的值为1的时候, 如果此时4个线程分别都抢到了资源, 那么它们都会处于休眠的状态, 此时, 不管哪个线程醒来, 它们执行流程如下:
                        2. 假设线程1先醒来, 会打印: 窗口1正在买第 1 张票, 之后tickets--, 即: tickets = 0
                        3. 假设线程2 醒来, 会打印: 窗口2正在买第 0 张票, 之后tickets--, 即: tickets = -1
                        4. 假设线程3 醒来, 会打印: 窗口3正在买第 -1 张票, 之后tickets--, 即: tickets = -2
                        5. 假设线程4 醒来, 会打印: 窗口4正在买第 -2 张票, 之后tickets--, 即: tickets = -3
                出现重复值:
                    核心点: tickets-- , 这行代码相当于: tickets = tickets - 1;  这行代码做了 3 件事儿:
                        1. 读值.  tickets = 66
                        2. 改值.  tickets - 1  = 66 - 1 = 65
                        3. 赋值.  tickets = 65
                     此时, 当该线程(窗口)打印完售票动作, 还没来得及修改tickets的值之前, 被别的线程抢走了CPU资源,
                     就会出现重复值的问题.
         */
    }
}
package com.it.demo02_tickets;
/*
    案例: 模拟卖票, 4个窗口卖100张票.
 */
public class Demo01 {
    public static void main(String[] args) {
        //1. 创建4个线程.
        MyThread mt1 = new MyThread("窗口1");
        MyThread mt2 = new MyThread("窗口2");
        MyThread mt3 = new MyThread("窗口3");
        MyThread mt4 = new MyThread("窗口4");
        //2. 开启线程, 卖票.
        mt1.start();
        mt2.start();
        mt3.start();
        mt4.start();
    }
}
 
package com.it.demo03_tickets;
//自定义的线程类, 用来模拟卖票
public class MyThread extends Thread {
    //1. 定义变量, 记录票数.
    private static int tickets = 100;       //细节1: 4个窗口共享100张票
    //2. 定义构造方法.
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }
    //3. 重写run()方法, 里边写是具体的卖票的逻辑.
    @Override
    public void run() {
        //具体的卖票的逻辑.
        while (true) {
            //一次卖票逻辑
            synchronized (MyThread.class) {  //锁对象一般会写: 该类的字节码文件
            //synchronized (this) {  //锁对象一般会写: 该类的字节码文件, 锁不住.
                //做越界处理, 没票就不卖了.
                if (tickets <= 0) {
                    break;
                }
                //加入休眠线程, 让程序出现非法值的概率大一些.
                try {
                    Thread.sleep(50);       //单位是毫秒         线程1, 线程2, 线程3, 线程4
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //具体的卖票
                System.out.println(getName() + " 正在售出第 " + tickets-- + " 张票");
            }
        }
    }
}
package com.it.demo03_tickets;
/*
    案例: 模拟卖票, 4个窗口卖100张票, 解决非法值问题.
    解决方案: 采用同步代码块解决
       格式:
            synchronized(锁对象) {
                //这里写的是要加锁的代码.
            }
       注意事项:
            1. 同步代码块的锁对象可以是 任意类型的对象.
            2. 必须使用同一把锁, 否则可能出现锁不住的情况.
    结论:
        线程安全(同步), 效率低,  线程不安全(不同步), 效率高.
 */
public class Demo01 {
    public static void main(String[] args) {
        //1. 创建4个线程.
        MyThread mt1 = new MyThread("窗口1");
        MyThread mt2 = new MyThread("窗口2");
        MyThread mt3 = new MyThread("窗口3");
        MyThread mt4 = new MyThread("窗口4");
        //2. 开启线程, 卖票.
        mt1.start();
        mt2.start();
        mt3.start();
        mt4.start();
    }
}
 
 
 
 
package com.it.demo04_tickets_runnable;
import com.it.demo03_tickets.MyThread;
//自定义的资源类, 用来模拟卖票.
public class MyRunnable implements Runnable {
    //1. 定义变量, 记录票数.
    private int tickets = 100;
    //2. 我们不用定义构造方法, 用系统默认的就行.
    //3. 重写run()方法, 里边写是具体的卖票的逻辑.
    @Override
    public void run() {
        //具体的卖票的逻辑.
        while (true) {
            //一次卖票逻辑
            //synchronized (MyRunnable.class) {   //可以
            synchronized (this) {                 //可以
                if (tickets <= 0) {
                    break;
                }
                //加入休眠线程, 让程序出现非法值的概率大一些.
                try {
                    Thread.sleep(50);       //单位是毫秒         线程1, 线程2, 线程3, 线程4
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //具体的卖票
                System.out.println(Thread.currentThread().getName() + " 正在售出第 " + tickets-- + " 张票");
            }
        }
    }
}
package com.it.demo04_tickets_runnable;
/*
    案例: 多线程模拟卖票,  实现Runnable接口版.
    实现Runnable接口 和 继承Thread版的 卖票代码相比, 以下4个地方可以优化:
        1. 定义票数的变量, 可以用不写 static, 因为资源对象就一个.
        2. 构造方法无需定义, 用系统默认提供的 无参构造即可.
        3. 锁对象可以是this, 因为资源对象就一个.
        4. 因为MyRunnable类和Thread类之间无关系, 所以不能直接调用 Thread#getName();
    线程同步简介:
        概述:
            多线程 并发 操作同一数据, 就有可能引发安全问题, 需要用到同步解决.
        特点:
            线程同步(安全), 效率低,  线程不同步(不安全), 效率高.
        分类:
            同步代码块:
                格式:
                    synchronized(锁对象) {
                        //这里写的是要加锁的代码.
                    }
               注意事项:
                    1. 同步代码块的锁对象可以是 任意类型的对象.
                    2. 必须使用同一把锁, 否则可能出现锁不住的情况.
            同步方法:
                说明: 就是在方法的声明上, 加上 synchronized 关键字.
                    静态同步方法:   锁对象是 该类的字节码文件对象, 即: 类名.class
                    非静态同步方法: 锁对象是 this
 */
public class Demo01 {
    public static void main(String[] args) {
        //1. 创建资源类对象.
        MyRunnable mr = new MyRunnable();
        //2. 创建线程对象.
        Thread th1 = new Thread(mr, "窗口1");
        Thread th2 = new Thread(mr, "窗口2");
        Thread th3 = new Thread(mr, "窗口3");
        Thread th4 = new Thread(mr, "窗口4");
        //3. 开启线程.
        th1.start();
        th2.start();
        th3.start();
        th4.start();
    }
    public synchronized void show1() {      //非静态同步方法, 锁对象: this
        System.out.println(1);
        System.out.println(2);
        System.out.println(3);
        System.out.println(4);
        System.out.println(5);
    }
    public synchronized static void show2() {    //静态同步方法, 锁对象: 该类的字节码文件
    }
}
 
 
 
 
package com.it.demo05_deadlock;
import com.sun.scenario.effect.LockableResource;
import java.util.concurrent.locks.Lock;
/*
    案例: 演示死锁的代码(只在面试用)
    大白话翻译:
        死锁指的是同步代码块的嵌套, 实际开发中, 我们在写代码的时候, 也要尽量规避这个问题.
    原理分析:
        1. 死锁需要两个线程, 两把锁.
        2. 一个线程先抢锁A, 后抢锁B, 另一个线程先抢锁B, 后抢锁A.
        3. 这个时候就有可能发生死锁的现象, 为了让效果更明显, 我们用while(true)改进.
 */
public class DeadLock {
    //1. 定义两把锁.
    private static final String LOCKA = "锁A";
    private static final String LOCKB = "锁B";
    public static void main(String[] args) {
        //2. 创建两个线程对象.
        //第一个线程对象
        new Thread("关羽") {
            @Override
            public void run() {
                //为了让效果更明显, 我们用while(true)改进.
                while (true) {
                    //一个线程先抢锁A, 后抢锁B,
                    synchronized (LOCKA) {
                        System.out.println(getName() + " 获取到 " + LOCKA + ", 等待 " + LOCKB);
                        synchronized (LOCKB) {
                            System.out.println(getName() + " 获取到 " + LOCKB + ", 成功进到小黑屋");
                        }
                    }
                }
            }
        }.start();
        //第二个线程对象
        new Thread("张飞") {
            @Override
            public void run() {
                //为了让效果更明显, 我们用while(true)改进.
                while (true) {
                    // 另一个线程先抢锁B, 后抢锁A.
                    synchronized (LOCKB) {
                        System.out.println(getName() + " 获取到 " + LOCKB + ", 等待 " + LOCKA);
                        synchronized (LOCKA) {
                            System.out.println(getName() + " 获取到 " + LOCKA + ", 成功进到小黑屋");
                        }
                    }
                }
            }
        }.start();
    }
}
package com.it.demo05_deadlock;
import java.util.Scanner;
/*
    案例: 演示IO流阻塞问题.
 */
public class Demo01 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请录入您的账号: ");
        String username = sc.nextLine();
        System.out.println(username);
    }
}
 
package com.it.demo06_priority;
//自定义的线程类
public class MyThread extends Thread{
    //构造方法
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + " ... " + i);
        }
    }
}
package com.it.demo06_priority;
/*
    案例: 演示多线程的优先级问题.
    涉及到的Thread类中的成员:
        成员常量:
            MAX_PRIORITY        10
            MIN_PRIORITY        1
            NORM_PRIORITY       5
        成员方法:
            public int getPriority();           获取当前线程的优先级.
            public void setPriority(int num);   设置当前线程的优先级.
    细节:
        1. 默认优先级为: 5, 范围是1-10.
        2. 线程的优先级越高, 只是说明该线程 抢到CPU资源的概率会更大, 并不代表它一定第一个执行.
 */
public class Demo01 {
    public static void main(String[] args) {
        //1. 创建3个线程对象.
        MyThread mt1 = new MyThread("飞机 .");
        MyThread mt2 = new MyThread("游艇 ..");
        MyThread mt3 = new MyThread("高铁 ... ");
        //2. 打印上述三个线程的默认优先级.
       /* System.out.println(mt1.getPriority());      //默认是: 5
        System.out.println(mt2.getPriority());
        System.out.println(mt3.getPriority());*/
       //3. 查看线程的优先级有哪些
        /*System.out.println(Thread.MAX_PRIORITY);        //10
        System.out.println(Thread.MIN_PRIORITY);        //1
        System.out.println(Thread.NORM_PRIORITY);      //5*/
        //4. 设置mt2线程的优先级为10
        //mt2.setPriority(Thread.MAX_PRIORITY);
        mt2.setPriority(10);
        //2. 开启线程
        mt1.start();
        mt2.start();
        mt3.start();
    }
}
 
package com.it.demo06_priority;
//自定义的线程类
public class MyThread extends Thread{
    //构造方法
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + " ... " + i);
        }
    }
}
package com.it.demo06_priority;
/*
    案例: 演示多线程的优先级问题.
    涉及到的Thread类中的成员:
        成员常量:
            MAX_PRIORITY        10
            MIN_PRIORITY        1
            NORM_PRIORITY       5
        成员方法:
            public int getPriority();           获取当前线程的优先级.
            public void setPriority(int num);   设置当前线程的优先级.
    细节:
        1. 默认优先级为: 5, 范围是1-10.
        2. 线程的优先级越高, 只是说明该线程 抢到CPU资源的概率会更大, 并不代表它一定第一个执行.
 */
public class Demo01 {
    public static void main(String[] args) {
        //1. 创建3个线程对象.
        MyThread mt1 = new MyThread("飞机 .");
        MyThread mt2 = new MyThread("游艇 ..");
        MyThread mt3 = new MyThread("高铁 ... ");
        //2. 打印上述三个线程的默认优先级.
       /* System.out.println(mt1.getPriority());      //默认是: 5
        System.out.println(mt2.getPriority());
        System.out.println(mt3.getPriority());*/
       //3. 查看线程的优先级有哪些
        /*System.out.println(Thread.MAX_PRIORITY);        //10
        System.out.println(Thread.MIN_PRIORITY);        //1
        System.out.println(Thread.NORM_PRIORITY);      //5*/
        //4. 设置mt2线程的优先级为10
        //mt2.setPriority(Thread.MAX_PRIORITY);
        mt2.setPriority(10);
        //2. 开启线程
        mt1.start();
        mt2.start();
        mt3.start();
    }
}
 
package com.it.demo07_join;
//自定义的线程类
public class MyThread extends Thread{
    //构造方法
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + " ... " + i);
        }
    }
}
package com.it.demo07_join;
/*
    案例: 演示 加入线程(相当于: 插队)
    涉及到的Thread类中的成员:
       public void join();      //等待这个线程死亡, 其实相当于插队, 只有这个线程执行完毕, 其他线程才会执行.
 */
public class Demo01 {
    public static void main(String[] args) {
        //1. 创建3个线程对象.
        MyThread mt1 = new MyThread("康熙");
        MyThread mt2 = new MyThread("四阿哥");
        MyThread mt3 = new MyThread("八阿哥");
        //2. 开启线程
        mt1.start();
        //设置mt1为: 加入线程, 即: 让它优先执行.
        try {
            mt1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mt2.start();
        mt3.start();
    }
}
 
package com.it.demo08_daemon;
//自定义的线程类
public class MyThread extends Thread{
    //构造方法
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + " ... " + i);
        }
    }
}
package com.it.demo08_daemon;
/*
    案例: 演示 守护线程.
    涉及到的Thread类中的成员:
       public void setDaemon(boolean flag);      //传入true: 表示设置当前线程为守护线程.
    细节:
        1. 线程默认都是非守护线程.
        2. 当非守护线程执行结束后, 所有与其关联的守护线程都会立马结束.
            //注意: 稍微会有一点点的延迟性. 
 */
public class Demo01 {
    public static void main(String[] args) {
        //1. 创建3个线程对象.
        MyThread mt1 = new MyThread("关羽");
        MyThread mt2 = new MyThread("张飞");
        //2. 设置上述的两个线程为: 守护线程.
        mt1.setDaemon(true);
        mt2.setDaemon(true);
        //3. 给当前线程(Main主线程)起个别名
        Thread.currentThread().setName("刘备");
        
        //4. 开启线程
        mt1.start();
        mt2.start();
        
        //5. 给主线程分配任务.
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "..." + i);
        }
    }
}
 
package com.it.demo09_lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//自定义的线程类, 用来模拟卖票
public class MyThread extends Thread {
    //1. 定义变量, 记录票数.
    private static int tickets = 100;       //细节1: 4个窗口共享100张票
    //定义一个Lock锁, 多态
    //Lock锁升级版知识点:  多线程的等待唤醒机制问题, 即: 让线程有规律的执行.
    static Lock lock = new ReentrantLock();     //必须保证同一把锁, 否则可能锁不住.
    //2. 定义构造方法.
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }
    //3. 重写run()方法, 里边写是具体的卖票的逻辑.
    @Override
    public void run() {
        //具体的卖票的逻辑.
        while (true) {
            //一次卖票逻辑
            //synchronized (MyThread.class) {  //锁对象一般会写: 该类的字节码文件
            lock.lock();            //加锁
            //做越界处理, 没票就不卖了.
            if (tickets <= 0) {
                break;
            }
            //加入休眠线程, 让程序出现非法值的概率大一些.
            try {
                Thread.sleep(50);       //单位是毫秒         线程1, 线程2, 线程3, 线程4
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //具体的卖票
            System.out.println(getName() + " 正在售出第 " + tickets-- + " 张票");
            lock.unlock();          //解锁
        }
    }
}
package com.it.demo09_lock;
/*
    案例: 演示Lock锁
    Lock锁简介:
        概述:
            它是JDK1.5的特性, 也是一个接口, 表示互斥锁, 可以让我们实现精准的加锁和解锁的事情.
        成员方法:
            unlock();   解锁
            lock();     加锁
 */
public class Demo01 {
    public static void main(String[] args) {
        //1. 创建4个线程.
        MyThread mt1 = new MyThread("窗口1");
        MyThread mt2 = new MyThread("窗口2");
        MyThread mt3 = new MyThread("窗口3");
        MyThread mt4 = new MyThread("窗口4");
        //2. 开启线程, 卖票.
        mt1.start();
        mt2.start();
        mt3.start();
        mt4.start();
    }
}
 
package com.it.demo10_executor;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/*
    案例: 线程池入门.
    线程池简介:
        概述:
            实际开发中, 当我们需要使用到大量生命周期短的线程对象时, 频繁的创建和销毁线程对象是非常消耗 系统资源的, 针对于这种情况,
            我们可以搞一个池子出来, 池子里边放一些线程对象, 用的时候去池子拿, 用完之后再返回去, 这个池子就叫: 线程池. 这样做的好处是,
            节约资源, 提高效率.
        成员方法:
            Executors 线程池工具类中的成员方法:
              public static ExecutorService newFixedThreadPool(int nThreads);   创建线程池对象,nThreads: 表示该池子中有几个线程对象.
           ExecutorService 它才是具体的 线程池类
             public Future<?> submit(Runnable task)     提交线程执行任务,这是实现多线程的第二种方式
             public Future<?> submit(Callable call)     提交线程执行任务,这是实现多线程的第三种方式
             public void shutdown()                     关闭线程池, 实际开发中, 正常情况下线程池是不关的.
          Future: 封装的是线程任务执行结束后, 具体的返回值.
            public V get();     获取线程任务执行结束后, 具体的返回值.
 */
public class Demo01 {
    public static void main(String[] args) throws Exception {
        //1. 创建线程池对象, 指定线程对象的个数.
        ExecutorService service = Executors.newFixedThreadPool(5);
        //2. 给线程池提交任务.
        Future<?> future = service.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("1. 我们只要把任务提交给线程池即可, 需要使用线程对象的时候, 它(线程池)会自动帮我们分配");
                System.out.println("2. 具体的任务可以写在: Runnable#run()方法中");
                System.out.println("3. 当线程对象使用完毕后, 线程池会自动回收它到线程池中.");
            }
        });
        //3. 获取线程任务执行结束后的结果.
        System.out.println(future.get());           //null
        //4. 关闭线程池. 实际开发中, 正常情况下线程池是不关的.
        service.shutdown();
    }
}
package com.it.demo10_executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/*
    案例: 线程池入门.
    线程池简介:
        概述:
            实际开发中, 当我们需要使用到大量生命周期短的线程对象时, 频繁的创建和销毁线程对象是非常消耗 系统资源的, 针对于这种情况,
            我们可以搞一个池子出来, 池子里边放一些线程对象, 用的时候去池子拿, 用完之后再返回去, 这个池子就叫: 线程池. 这样做的好处是,
            节约资源, 提高效率.
        成员方法:
            Executors 线程池工具类中的成员方法:
              public static ExecutorService newFixedThreadPool(int nThreads);   创建线程池对象,nThreads: 表示该池子中有几个线程对象.
           ExecutorService 它才是具体的 线程池类
             public Future<?> submit(Runnable task)     提交线程执行任务,这是实现多线程的第二种方式
             public Future<?> submit(Callable call)     提交线程执行任务,这是实现多线程的第三种方式
             public void shutdown()                     关闭线程池, 实际开发中, 正常情况下线程池是不关的.
          Future: 封装的是线程任务执行结束后, 具体的返回值.
            public V get();     获取线程任务执行结束后, 具体的返回值.
     Runnable接口和Callable接口的区别:
        1. 作用范围不同.
            Runnable可以不结合线程池, 直接单独使用.
            Callable必须结合线程池相结合使用.
        2. 接口中的方法不同.
            Runnable#run(), 有异常只能try, 且该方法没有返回值.
            Callable#call(), 可以throws抛出异常, 且该方法可以有返回值.
 */
public class Demo02 {
    public static void main(String[] args) throws Exception {
        //1. 创建线程池对象, 指定线程对象的个数.
        ExecutorService service = Executors.newFixedThreadPool(5);
        //2. 给线程池提交任务.
        //Future<?> future = service.submit(可以是Runnable接口的子类对象);
        //Future<?> future = service.submit(也可以是Callable接口的子类对象);
        Future<?> future = service.submit(new MyCallable());
        //3. 获取线程任务执行结束后的结果.
        System.out.println(future.get());           //风和日丽, 晴空万里
        //4. 关闭线程池. 实际开发中, 正常情况下线程池是不关的.
        service.shutdown();
    }
}
package com.it.demo10_executor;
import java.util.concurrent.Callable;
//自定义的资源类, 实现Callable接口
public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("我是Callable接口封装的 具体任务");
        return "风和日丽, 晴空万里";
    }
}
 
package com.it.demo11_signature;
//定义共享数据区, 奶箱
public class Box {
    //定义变量, 记录当前是第几瓶奶
    private int milk;
    //定义变量, 记录奶箱的状态
    boolean flag = false;       //true: 有奶, false: 无奶
    //定义方法, 放奶
    public synchronized void put(int milk) {
        //判断奶箱是否有牛奶
        if (flag) {
            //有牛奶, 就等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //具体的放牛奶的逻辑.
        this.milk = milk;
        System.out.println("送奶工正在放入第 "+ milk +" 瓶奶");
        //修改奶箱状态.
        flag = true;
        //唤醒消费者, 取奶.
        this.notify();
    }
    //定义方法, 取奶.
    public synchronized void get() {
        //判断奶箱是否有奶
        if (!flag) {
            //无奶就等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //具体取奶的动作
        System.out.println("消费者正在获取第 " + milk + " 瓶奶");
        //修改奶箱状态.
        flag = false;
        //唤醒生产者, 放奶.
        this.notify();
    }
}
package com.it.demo11_signature;
import java.util.Properties;
/*
    案例: 演示消费者设计模式.
    需求:
        奶箱类(Box):定义一个成员变量,表示第x瓶奶,提供存储牛奶和获取牛奶的操作
        生产者类(Producer):实现Runnable接口,重写run()方法,调用存储牛奶的操作
        消费者类(Customer):实现Runnable接口,重写run()方法,调用获取牛奶的操作
        测试类(BoxDemo):里面有main方法,main方法中的代码步骤如下
        ①创建奶箱对象,这是共享数据区域
        ②创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作
        ③创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
        ④创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
        ⑤启动线程
    设计模式简介:
        概述:
            设计模式不属于任何语言, 他也不是语法, 而是前辈们总结的用来解决问题的思路和方案, 它是一套用来解决一系列问题的技术方案.
        分类: 一共23种
            创建型:  需要创建对象, 5种
                单例设计模式,
                工厂方法设计模式
            结构型:  用来描述类与类之间的关系的 7种
                装饰设计模式,
                    BufferedReader br = new BufferedReader(new FileReader("1.txt));
                适配器设计模式
            行为型:  指的是事物能够做什么        11种
                模板方法设计模式
                消费者设计模式
        推荐一本:
            大话设计模式(Java版)
 */
public class BoxTest {
    public static void main(String[] args) {
        //①创建奶箱对象,这是共享数据区域
        Box b = new Box();
        //②创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作
        Producer p = new Producer(b);
        //③创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
        Customer c = new Customer(b);
        //④创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
        Thread th1 = new Thread(p);     //负责放牛奶的 生产者
        Thread th2 = new Thread(c);     //负责获取牛奶的 消费者
        //⑤启动线程
        th1.start();
        th2.start();
    }
}
 
package com.it.demo11_signature;
//定义消费者类, 实现Runnable接口, 充当资源类
public class Customer implements Runnable{
    //获取奶箱的引用
    private Box box;
    //创建消费者对象的时候, 必须指定 奶箱.
    public Customer(Box box) {
        this.box = box;
    }
    @Override
    public void run() {
        //取牛奶的动作
        while (true)
            box.get();
    }
}
package com.it.demo11_signature;
//定义生产者类, 实现Runnable接口, 充当资源类
public class Producer implements Runnable{
    //获取奶箱的引用
    private Box box;
    //创建生产者对象的时候, 必须指定 奶箱.
    public Producer(Box box) {
        this.box = box;
    }
    @Override
    public void run() {
        //具体放牛奶的动作
        for (int i = 1; i <= 10; i++) {
            box.put(i);
        }
    }
}
 
package cn.it.demo;
/*
 *  定义子类,继承Thread 
 *  重写方法run 
 */
public class SubThread  extends Thread{
    public void run(){
        for(int i = 0; i < 50;i++){
            System.out.println("run..."+i);
        }
    }
}
package cn.it.demo;
/*
 *  程序中的主线程
 */
public class Demo {
    public static void main(String[] args) {
        System.out.println(0/0);
        function();
        System.out.println(Math.abs(-9));
    }
    
    public static void function(){
        for(int i = 0 ; i < 10000;i++){
            System.out.println(i);
        }
    }
}
package cn.it.demo;
/*
 * 创建和启动一个线程
 *   创建Thread子类对象
 *   子类对象调用方法start()
 *      让线程程序执行,JVM调用线程中的run
 */
public class ThreadDemo {
    public static void main(String[] args) {
        SubThread st = new SubThread();
        SubThread st1 = new SubThread();
        st.start();
        st1.start();
        for(int i = 0; i < 50;i++){
            System.out.println("main..."+i);
        }
    }
}
 
package cn.it.demo1;
/*
 *  获取线程名字,父类Thread方法
 *    String getName()
 */
public class NameThread extends Thread{
    
    public NameThread(){
        super("小强");
    }
    
    public void run(){
        System.out.println(getName());
    }
}
package cn.it.demo1;
/*
 *  每个线程,都有自己的名字
 *  运行方法main线程,名字就是"main"
 *  其他新键的线程也有名字,默认 "Thread-0","Thread-1"
 *  
 *  JVM开启主线程,运行方法main,主线程也是线程,是线程必然就是
 *  Thread类对象
 *  Thread类中,静态方法
 *   static Thread currentThread()返回正在执行的线程对象
 */
public class ThreadDemo {
    public static void main(String[] args) {
        NameThread nt = new NameThread();
        nt.setName("旺财");
        nt.start();
        
        /*Thread t =Thread.currentThread();
        System.out.println(t.getName());*/
        System.out.println(Thread.currentThread().getName());
    }
}
 
package cn.it.demo2;
public class SleepThread extends Thread{
    public void run(){
        for(int i = 0 ; i < 5 ;i++){
            try{
                Thread.sleep(500);
            }catch(Exception ex){
                
            }
            System.out.println(i);
        }
    }
}
package cn.it.demo2;
public class ThreadDemo {
    public static void main(String[] args) throws Exception{
        /*for(int i = 0 ; i < 5 ;i++){
            Thread.sleep(50);
            System.out.println(i);
        }*/
        
        new SleepThread().start();
    }
}
 
package cn.it.demo3;
/*
 *  实现线程成功的另一个方式,接口实现
 *  实现接口Runnable,重写run方法
 */
public class SubRunnable implements Runnable{
    public void run(){
        for(int i = 0 ; i < 50; i++){
            System.out.println("run..."+i);
        }
    }
}
package cn.it.demo3;
/*
 *  实现接口方式的线程
 *    创建Thread类对象,构造方法中,传递Runnable接口实现类
 *    调用Thread类方法start()
 */
public class ThreadDemo {
    public static void main(String[] args) {
        SubRunnable sr = new SubRunnable();
        Thread t = new Thread(sr);
        t.start();
        for(int i = 0 ; i < 50; i++){
            System.out.println("main..."+i);
        }
    }
}
 
package cn.it.demo4;
/*
 *  使用匿名内部类,实现多线程程序
 *  前提: 继承或者接口实现
 *  new 父类或者接口(){
 *     重写抽象方法
 *  }
 */
public class ThreadDemo {
    public static void main(String[] args) {
        //继承方式  XXX extends Thread{ public void run(){}}
        new Thread(){
            public void run(){
                System.out.println("!!!");
            }
        }.start();
        
        //实现接口方式  XXX implements Runnable{ public void run(){}}
        
        Runnable r = new Runnable(){
            public void run(){
                System.out.println("###");
            }
        };
        new Thread(r).start();
        
        
        new Thread(new Runnable(){
            public void run(){
                System.out.println("@@@");
            }
        }).start();
        
    }
}
 
package cn.it.demo5;
/*
 * Callable 接口的实现类,作为线程提交任务出现
 * 使用方法返回值
 */
import java.util.concurrent.Callable;
public class ThreadPoolCallable implements Callable<String>{
    public String call(){
        return "abc";
    }
}
package cn.it.demo5;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
 *  JDK1.5新特性,实现线程池程序
 *  使用工厂类 Executors中的静态方法创建线程对象,指定线程的个数
 *   static ExecutorService newFixedThreadPool(int 个数) 返回线程池对象
 *   返回的是ExecutorService接口的实现类 (线程池对象)
 *   
 *   接口实现类对象,调用方法submit (Ruunable r) 提交线程执行任务
 *          
 */
public class ThreadPoolDemo {
    public static void main(String[] args) {
        //调用工厂类的静态方法,创建线程池对象
        //返回线程池对象,是返回的接口
        ExecutorService es = Executors.newFixedThreadPool(2);
        //调用接口实现类对象es中的方法submit提交线程任务
        //将Runnable接口实现类对象,传递
        es.submit(new ThreadPoolRunnable());
        es.submit(new ThreadPoolRunnable());
        es.submit(new ThreadPoolRunnable());
    
    }
}
package cn.it.demo5;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/*
 *  实现线程程序的第三个方式,实现Callable接口方式
 *  实现步骤
 *    工厂类 Executors静态方法newFixedThreadPool方法,创建线程池对象
 *    线程池对象ExecutorService接口实现类,调用方法submit提交线程任务
 *    submit(Callable c)
 */
public class ThreadPoolDemo1 {
    public static void main(String[] args)throws Exception {
        ExecutorService es = Executors.newFixedThreadPool(2);
        //提交线程任务的方法submit方法返回 Future接口的实现类
        Future<String> f = es.submit(new ThreadPoolCallable());
        String s = f.get();
        System.out.println(s);
    }
}
package cn.it.demo5;
public class ThreadPoolRunnable implements Runnable {
    public void run(){
        System.out.println(Thread.currentThread().getName()+" 线程提交任务");
    }
}
 
package cn.it.demo6;
import java.util.concurrent.Callable;
public class GetSumCallable implements Callable<Integer>{
    private int a;
    public GetSumCallable(int a){
        this.a=a;
    }
    
    public Integer call(){
        int sum = 0 ;
        for(int i = 1 ; i <=a ; i++){
            sum = sum + i ;
        }
        return sum;
    }
}
package cn.it.demo6;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/*
 * 使用多线程技术,求和
 * 两个线程,1个线程计算1+100,另一个线程计算1+200的和
 * 多线程的异步计算
 */
public class ThreadPoolDemo {
    public static void main(String[] args)throws Exception {
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<Integer> f1 =es.submit(new GetSumCallable(100));
        Future<Integer> f2 =es.submit(new GetSumCallable(200));
        System.out.println(f1.get());
        System.out.println(f2.get());
        es.shutdown();
    }
}
 
 
 
 
package cn.it.demo;
/*
 * 多线程并发访问同一个数据资源
 * 3个线程,对一个票资源,出售
 */
public class ThreadDemo {
    public static void main(String[] args) {
        //创建Runnable接口实现类对象
        Tickets t = new Tickets();
        //创建3个Thread类对象,传递Runnable接口实现类
        Thread t0 = new Thread(t);
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        
        t0.start();t1.start();t2.start();
        
    }
}
package cn.it.demo;
/*
 *  通过线程休眠,出现安全问题
 *  解决安全问题,Java程序,提供技术,同步技术
 *  公式:
 *    synchronized(任意对象){
 *      线程要操作的共享数据
 *    }
 *    同步代码块
 */
public class Tickets implements Runnable{
    
    //定义出售的票源
    private int ticket = 100;
    private Object obj = new Object();
    
    public void run(){
        while(true){
            //线程共享数据,保证安全,加入同步代码块
            synchronized(obj){
            //对票数判断,大于0,可以出售,变量--操作
                if( ticket > 0){
                    try{
                       Thread.sleep(10);
                    }catch(Exception ex){}
                    System.out.println(Thread.currentThread().getName()+" 出售第 "+ticket--);
                }
            }
        }
    }
}
 
package cn.it.demo1;
/*
 * 多线程并发访问同一个数据资源
 * 3个线程,对一个票资源,出售
 */
public class ThreadDemo {
    public static void main(String[] args) {
        //创建Runnable接口实现类对象
        Tickets t = new Tickets();
        //创建3个Thread类对象,传递Runnable接口实现类
        Thread t0 = new Thread(t);
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        
        t0.start();t1.start();t2.start();
        
    }
}
package cn.it.demo1;
/*
 *  采用同步方法形式,解决线程的安全问题
 *  好处: 代码简洁
 *  将线程共享数据,和同步,抽取到一个方法中
 *  在方法的声明上,加入同步关键字
 *  
 *  问题:
 *    同步方法有锁吗,肯定有,同步方法中的对象锁,是本类对象引用 this
 *    如果方法是静态的呢,同步有锁吗,绝对不是this
 *    锁是本类自己.class 属性
 *    静态方法,同步锁,是本类类名.class属性
 */
public class Tickets implements Runnable{
    //定义出售的票源
    private  int ticket = 100;
    
    public void run(){
        while(true){
            payTicket();
        }
    }
    
    public  synchronized void payTicket(){    
            if( ticket > 0){
                try{
                   Thread.sleep(10);
                }catch(Exception ex){}
                System.out.println(Thread.currentThread().getName()+" 出售第 "+ticket--);
            }
        
    }
}
 
package cn.it.demo2;
/*
 * 多线程并发访问同一个数据资源
 * 3个线程,对一个票资源,出售
 */
public class ThreadDemo {
    public static void main(String[] args) {
        //创建Runnable接口实现类对象
        Tickets t = new Tickets();
        //创建3个Thread类对象,传递Runnable接口实现类
        Thread t0 = new Thread(t);
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        
        t0.start();t1.start();t2.start();
        
    }
}
package cn.it.demo2;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
 *  使用JDK1.5 的接口Lock,替换同步代码块,实现线程的安全性
 *  Lock接口方法:
 *     lock() 获取锁
 *     unlock()释放锁
 *  实现类ReentrantLock
 */
public class Tickets implements Runnable{
    
    //定义出售的票源
    private int ticket = 100;
    //在类的成员位置,创建Lock接口的实现类对象
    private Lock lock = new ReentrantLock();
    
    public void run(){
        while(true){
            //调用Lock接口方法lock获取锁
            lock.lock();
            //对票数判断,大于0,可以出售,变量--操作
                if( ticket > 0){
                    try{
                       Thread.sleep(10);
                       System.out.println(Thread.currentThread().getName()+" 出售第 "+ticket--);
                    }catch(Exception ex){
                        
                    }finally{
                        //释放锁,调用Lock接口方法unlock
                        lock.unlock();
                    }
                }
    
            
        }
    }
}
 
package cn.itdemo3;
public class DeadLock implements Runnable{
    private int i = 0;
    public void run(){
        while(true){
            if(i%2==0){
                //先进入A同步,再进入B同步
                synchronized(LockA.locka){
                    System.out.println("if...locka");
                    synchronized(LockB.lockb){
                        System.out.println("if...lockb");
                    }
                }
            }else{
                //先进入B同步,再进入A同步
                synchronized(LockB.lockb){
                    System.out.println("else...lockb");
                    synchronized(LockA.locka){
                        System.out.println("else...locka");
                    }
                }
            }
            i++;
        }
    }
}
package cn.it.demo3;
public class DeadLockDemo {
    public static void main(String[] args) {
        DeadLock dead = new DeadLock();
        Thread t0 = new Thread(dead);
        Thread t1 = new Thread(dead);
        t0.start();
        t1.start();
    }
}
package cn.it.demo3;
public class LockA {
    private LockA(){}
    
    public  static final LockA locka = new LockA();
}
package cn.it.demo3;
public class LockB {
    private LockB(){}
    
    public static final LockB lockb = new LockB();
}
 
package cn.it.demo4;
/*
 *  输入的线程,对资源对象Resource中成员变量赋值
 *  一次赋值 张三,男
 *  下一次赋值 lisi,nv
 */
public class Input implements Runnable {
    private Resource r ;
    
    public Input(Resource r){
        this.r = r;
    }
    
    public void run() {
        int i = 0 ;
        while(true){
          synchronized(r){
              //标记是true,等待
                if(r.flag){
                    try{r.wait();}catch(Exception ex){}
                }
              
                if(i%2==0){
                    r.name = "张三";
                    r.sex = "男";
                }else{
                    r.name = "lisi";
                    r.sex = "nv";
                }
                //将对方线程唤醒,标记改为true
                r.flag = true;
                r.notify();
          }
            i++;
        }
    }
}
package cn.it.demo4;
/*
 *  输出线程,对资源对象Resource中成员变量,输出值
 */
public class Output implements Runnable {
    private Resource r ;
    
    public Output(Resource r){
        this.r = r;
    }
    public void run() {
        while(true){
          synchronized(r){    
              //判断标记,是false,等待
            if(!r.flag){
                try{r.wait();}catch(Exception ex){}
            }
            System.out.println(r.name+".."+r.sex);
            //标记改成false,唤醒对方线程
            r.flag = false;
            r.notify();
          }
        }
    }
}
package cn.it.demo4;
/*
 *  定义资源类,有2个成员变量
 *  name,sex
 *  同时有2个线程,对资源中的变量操作
 *  1个对name,age赋值
 *  2个对name,age做变量的输出打印
 */
public class Resource {
    public String name;
    public String sex;
    public boolean flag = false;
}
package cn.it.demo4;
/*
 *  开启输入线程和输出线程,实现赋值和打印值
 */
public class ThreadDemo{
    public static void main(String[] args) {
        
        Resource r = new Resource();
        
        Input in = new Input(r);
        Output out = new Output(r);
        
        Thread tin = new Thread(in);
        Thread tout = new Thread(out);
        
        tin.start();
        tout.start();
    }
}