一、实现多线程
1、继承Thread类
| 方法名 | 说明 | 
|---|---|
| void run() | 在线程开启后,此方法将被调用执行 | 
| void start() | 使此线程开始执行,Java虚拟机会调用run方法() | 
- run()方法和start()方法的区别? 
 - run():封装线程执行的代码,直接调用,相当于普通方法的调用
 - start():启动线程;然后由JVM调用此线程的run()方法
- 
实现步骤 - 定义一个类MyThread继承Thread类
- 在MyThread类中重写run()方法
- 创建MyThread类的对象
- 启动线程
 
//实现多线程
 class MyThread extends Thread {
    @Override
    public void run() {
        // 当前线程
        String name = Thread.currentThread().getName();
        System.out.println(name);
        //代码就是线程在开启之后执行的代码
        for (int i = 0; i < 3; i++) {
            System.out.println("线程开启了" + i);
        }
    }
}
// 使用
public class Demo {
    public static void main(String[] args) {
        // 当前线程
        String name = Thread.currentThread().getName();
        System.out.println(name);
        //创建一个线程对象
        MyThread t1 = new MyThread();
      //表示的仅仅是创建对象,用对象去调用方法,并没有开启线程
        t1.run();
        //开启一条线程
        t1.start();
    }
}
2、实现Runnable接口
| 方法名 | 说明 | 
|---|---|
| Thread(Runnable target) | 分配一个新的Thread对象 | 
| Thread(Runnable target, String name) | 分配一个新的Thread对象 | 
- 
实现步骤 - 定义一个类MyRunnable实现Runnable接口
- 在MyRunnable类中重写run()方法
- 创建MyRunnable类的对象
- 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
- 启动线程
 
// 实现Runnable接口
class MyRunnable implements Runnable{
    @Override
    public void run() {
        // 当前线程
        String name = Thread.currentThread().getName();
        System.out.println(name);
        //线程启动后执行的代码
        for (int i = 0; i < 3; i++) {
            System.out.println( "Runnable:" + i);
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        // 当前线程
        String name = Thread.currentThread().getName();
        System.out.println(name);
        //创建了一个参数的对象
        MyRunnable mr = new MyRunnable();
        //创建了一个线程对象,并把参数传递给这个线程.
        //在线程启动之后,执行的就是参数里面的run方法
        Thread t1 = new Thread(mr);
        //开启线程
        t1.start();
    }
}
3.实现Callable接口
| 方法名 | 说明 | 
|---|---|
| V call() | 计算结果,如果无法计算结果,则抛出一个异常 | 
| FutureTask(Callable<V> callable) | 创建一个 FutureTask,一旦运行就执行给定的 Callable | 
| V get() | 如有必要,等待计算完成,然后获取其结果 | 
- 
实现步骤 - 定义一个类MyCallable实现Callable接口
- 在MyCallable类中重写call()方法
- 创建MyCallable类的对象
- 创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数
- 创建Thread类的对象,把FutureTask对象作为构造方法的参数
- 启动线程
- 再调用get方法,就可以获取线程结束之后的结果。
 
/ 实现Callable接口
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        // 当前线程
        String name = Thread.currentThread().getName();
        System.out.println(name);
        for (int i = 0; i < 3; i++) {
            System.out.println("Callable:" + i);
        }
        //返回值就表示线程运行完毕之后的结果
        return "答应";
    }
}
public class Demo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 当前线程
        String name = Thread.currentThread().getName();
        System.out.println(name);
        //线程开启之后需要执行里面的call方法
        MyCallable mc = new MyCallable();
        //可以获取线程执行完毕之后的结果.也可以作为参数传递给Thread对象
        FutureTask<String> ft = new FutureTask<>(mc);
        //创建线程对象
        Thread t1 = new Thread(ft);
        //开启线程
        t1.start();
        String s = ft.get();
        System.out.println(s);
    }
}
4.三种实现方式的对比
- 实现Runnable、Callable接口
- 好处: 扩展性强,实现该接口的同时还可以继承其他的类
- 缺点: 编程相对复杂,不能直接使用Thread类中的方法
 
- 继承Thread类
- 好处: 编程比较简单,可以直接使用Thread类中的方法
- 缺点: 可以扩展性较差,不能再继承其他的类
 
二、多线程的使用
1.设置和获取线程名称
| 方法名 | 说明 | 
|---|---|
| void setName(String name) | 将此线程的名称更改为等于参数name | 
| String getName() | 返回此线程的名称 | 
| Thread currentThread() | 返回对当前正在执行的线程对象的引用 | 
| static void sleep(long millis) | 使当前正在执行的线程停留(暂停执行)指定的毫秒数 | 
| final int getPriority() | 返回此线程的优先级 | 
| final void setPriority(int newPriority) | 更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10 | 
| void setDaemon(boolean on) | 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出 | 
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            try {
                // 不同线程睡眠时间不同,观察控制台打印
                String name = Thread.currentThread().getName();
                if (name.equals("1")){
                    Thread.sleep(10);
                }else{
                    Thread.sleep(20);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
    }
}
public class Demo {
    public static void main(String[] args) throws InterruptedException {
        MyRunnable mr = new MyRunnable();
        //线程是有默认名字的,格式:Thread-编号
        // 设置线程名
        Thread t1 = new Thread(mr,"1");
        Thread t2 = new Thread(mr,"2");
      
  //设置优先级: 1 - 10 默认值:5
        t1.setPriority(10);
        t2.setPriority(1);
        
        t1.start();
        t2.start();
    }
}
2.线程锁:synchronized
- 安全问题出现的条件
- 是多线程环境
- 有共享数据
- 有多条语句操作共享数据
 
- 同步代码块格式:
  synchronized(任意对象) { 
    多条语句操作共享数据的代码 
  }
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
public class MyThread extends Thread {
    private static int ticketCount = 100;
    private static Object obj = new Object();
    @Override
    public void run() {
        while(true){
            synchronized (obj){ //就是当前的线程对象.
                if(ticketCount <= 0){
                    //卖完了
                    break;
                }else{
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticketCount--;
                    System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");
                }
            }
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.setName("窗口一");
        t2.setName("窗口二");
        t1.start();
        t2.start();
    }
}
2.线程锁:ReentrantLock
| 方法名 | 说明 | 
|---|---|
| ReentrantLock() | 创建一个ReentrantLock的实例 | 
- 
加锁解锁方法 方法名 说明 void lock() 获得锁 void unlock() 释放锁 
public class Ticket implements Runnable {
    //票的数量
    private int ticket = 100;
    private Object obj = new Object();
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            lock.lock();
            if (ticket <= 0) {
                //卖完了
                break;
            } else {
                ticket--;
                System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
            }
            lock.unlock();
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread t1 = new Thread(ticket);
        Thread t2 = new Thread(ticket);
        Thread t3 = new Thread(ticket);
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}
3..生产者消费者
- 
Object类的等待和唤醒方法 方法名 说明 void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法 void notify() 唤醒正在等待对象监视器的单个线程 void notifyAll() 唤醒正在等待对象监视器的所有线程