JAVA 多线程、网络编程、反射、动态代理

一、多线程

线程其实是程序中的一条执行路径。
    程序中就有多条执行路径,每一条执行执行路径就是一条线程,所以这样的程序就是多线程程序。

Thread的常用方法

//Thread提供的常用方法
public void run()//线程的任务方法
public void start()//启动线程
public String getName()//获取当前线程的名称,线程名称默认是Thread-索引
public void setName()//为线程设置名称
public static Therad currentThread()//获取当前执行的线程对象
public static void sleep(long time)//让当前执行的线程休眠多少毫秒后,再继续执行
public final void join()//让调用当前这个方法的线程先执行完
 /*********************************************************************/
//Thread提供的常见构造器
public Thread(String name)//可以为当前线程指定名称
public Thread(Runnable target)//封装Runnable对象成为线程对象
public Thread(Runnable target,String name)//封装Runnable对象成为线程对象,并指定线程名称。
    Thread类还通知:yield、interrupt、守护线程、线程优先级等线程的控制方法。
​

1、三种线程创建方式及优缺点

/*一、继承Thread类创建线程类
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程。
*/  
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
    }
    static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println("aaaaa");
        }
    }   
    
/***************************************************************************/
/*二、通过Runnable接口创建线程类
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
*/
    
    public static void main(String[] args) {
        newThread thread = new newThread();
        new Thread(thread).start();
    }
    static class newThread implements  Runnable{
        @Override
        public void run() {
            System.out.println("bbbb");
        }
    }
    
/*************************************************************************/
/*三、通过Callable和Future创建线程
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
*/  
    public static void main(String[] args) {
        callableThread ct = new callableThread();
        FutureTask task = new FutureTask(ct);
        new Thread(task).start();
    }
 
    static class callableThread implements Callable {
        @Override
        public Object call() throws Exception {
            System.out.println(Thread.currentThread().getName());
            return 2;
        }
    }
    
/**************************************************************************/
创建三种线程方式的对比:
  1、采用实现Runnable、Callable接口的方式创见多线程。
  优势是:
    ● 线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
    ● 在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
  劣势是:
    ● 编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。
      
/*************************************************************************/
  2、使用继承Thread类的方式创建多线程。
   优势是:
    ● 编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
    劣势是:
    ● 线程类已经继承了Thread类,所以不能再继承其他父类。

2、线程同步方案

/*
    每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动释放锁,然后其他线程才能再加锁进来。
  ● 多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题
    1、线程安全问题出现的原因?
       ● 存在多个线程在同时执行
       ● 同时访问一个共享资源
       ● 存在修改改共享资源
*/ 
/****************************************************************************/
&& 同步代码块:
    
       ● 作用:把访问共享资源的核心代码给上锁,以此保证线程安全。
            synchronized(同步锁){
            访问共享资源的核心代码
}
       ● 原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程可以进来执行。
  同步锁的注意事项:
       ● 对于当前同时执行的线程来说,同步锁必须是一把(同一个对象),否则会出现bug。
/**************************************************************************/   
&& 同步方法:
           
      ● 作用:把访问共享资源的核心方法给上锁,以此保证线程安全。
         修饰符synchronized返回值类型 方法名称(形参列表){
           操作共享资源的代码
       }    
      ● 原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。
   同步方法的底层原理:
      ● 同步方法其实底层也是有隐式锁对象的,只是所得范围  
      ● 如果方法是实例方法:同步方法默认用this作为的锁对象。
      ● 如果方法是静态方法:同步方法默认用类名.class作为的锁对象。
/**************************************************************************/          
&&:是同步代码块好还是同步方法好一点?
      ● 范围上:同步代码块锁的范围更小,同步方法锁的范围更大。
      ● 可读性:同步方法更好。
/*****************************************************************************/
Lock锁:      
     Lock锁是JDK5版本专门提供的一种锁对象,通过这个锁对象的方法来达到加锁,和释放锁的目的,使用起来更加灵活。
1.首先在成员变量位子,需要创建一个Lock接口的实现类对象(这个对象就是锁对象)
    private final Lock lk = new ReentrantLock();
2.在需要上锁的地方加入下面的代码
     lk.lock(); // 加锁
     //...中间是被锁住的代码...
     lk.unlock(); // 解锁
/**************************************************************************/
           lock.lock();
        try {
            if (!flag){//正
                System.out.println("XXX线程正在执行任务B.......");
            }
            flag=true;
        } finally {
            lock.unlock();
        }

3、线程通信

★ 线程通信
    当多个线程共同操作共享资源时,线程间通过某种方式互相告知自己的状态,以相互协调,避免无效的资源挣抢。
  线程通信的常见模式:是生产者与消费者模型
● 生产者线程负责生成数据
● 消费者线程负责消费生产者生成的数据
● 注意:生产者生产完数据后应该让自己等待,通知其他消费者消费;消费者消费完数据之后应该让自己等待,同时通知生产者生成。
    /************************************************************************************/
    案例:
  1.先确定在这个案例中,什么是共享数据?
    答:这里案例中桌子是共享数据,因为厨师和顾客都需要对桌子上的包子进行操作。
​
2.再确定有那几条线程?哪个是生产者,哪个是消费者?
    答:厨师是生产者线程,3条生产者线程; 
       顾客是消费者线程,2条消费者线程
       
3.什么时候将哪一个线程设置为什么状态
    生产者线程(厨师)放包子:
         1)先判断是否有包子
         2)没有包子时,厨师开始做包子, 做完之后把别人唤醒,然后让自己等待
         3)有包子时,不做包子了,直接唤醒别人、然后让自己等待
            
    消费者线程(顾客)吃包子:
         1)先判断是否有包子
         2)有包子时,顾客开始吃包子, 吃完之后把别人唤醒,然后让自己等待
         3)没有包子时,不吃包子了,直接唤醒别人、然后让自己等待
  /***********************************************************************/
  按照思路分析代码如下:
 public class Desk {
    private List list = new ArrayList<>();
​
    // 放1个包子的方法
    // 厨师1 厨师2 厨师3
    public synchronized void put() {
        try {
            String name = Thread.currentThread().getName();
            // 判断是否有包子。
            if(list.size() == 0){
                list.add(name + "做的肉包子");
                System.out.println(name + "做了一个肉包子~~");
                Thread.sleep(2000);
​
                // 唤醒别人, 等待自己
                this.notifyAll();
                this.wait();
            }else {
                // 有包子了,不做了。
                // 唤醒别人, 等待自己
                this.notifyAll();
                this.wait();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
​
    // 吃货1 吃货2
    public synchronized void get() {
        try {
            String name = Thread.currentThread().getName();
            if(list.size() == 1){
                // 有包子,吃了
                System.out.println(name  + "吃了:" + list.get(0));
                list.clear();
                Thread.sleep(1000);
                this.notifyAll();
                this.wait();
            }else {
                // 没有包子
                this.notifyAll();
                this.wait();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}  
/**************************************************************************/
测试类:
    
    public class ThreadTest {
    public static void main(String[] args) {
        //   需求:3个生产者线程,负责生产包子,每个线程每次只能生产1个包子放在桌子上
        //      2个消费者线程负责吃包子,每人每次只能从桌子上拿1个包子吃。
        Desk desk  = new Desk();
​
        // 创建3个生产者线程(3个厨师)
        new Thread(() -> {
            while (true) {
                desk.put();
            }
        }, "厨师1").start();
​
        new Thread(() -> {
            while (true) {
                desk.put();
            }
        }, "厨师2").start();
​
        new Thread(() -> {
            while (true) {
                desk.put();
            }
        }, "厨师3").start();
​
        // 创建2个消费者线程(2个吃货)
        new Thread(() -> {
            while (true) {
                desk.get();
            }
        }, "吃货1").start();
​
        new Thread(() -> {
            while (true) {
                desk.get();
            }
        }, "吃货2").start();
    }
}
/******************************************************************/
执行上面代码,运行结果你会发现多个线程相互协调执行,避免无效的资源挣抢。

4、线程池

线程池就是一个可以复用线程的技术。
    把一个或多个线程通过统一的方式进行调度和重复使用的技术,避免了因为线程过多而带来使用上的开销。
    作用:
    ● 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
    ● 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
    ● 提高线程的可管理性。
    
 创建线程池
    在JDK5版本中提供了代表线程池的接口ExecutorService,而这个接口下有一个实现类叫ThreadPoolExecutor类,使用ThreadPoolExecutor类就可以用来创建线程池对象。
    ThreadPoolExecutor构造器
    public ThreadPoolExecutor(int corePoolsize ,int maxmumPoolSize,long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue,ThreadFactory threadFactory,RejectedExecutionHamdler handler)
  ● 参数一:corePoolSize:指定线程池的核心线程数量。正式工:3
  ● 参数二:maximumPoolSize:指定线程池的最大线程数量。
  ● 参数三:keepAliveTime:指定临时线程的存活时间。
  ● 参数四:unit:指定临时线程存活的时间单位(秒、分、时、天)
  ● 参数五:workQueue:指定线程池的任务队列。
  ● 参数六:threadFactory:指定线程池的线程工厂。
  ● 参数七:handler:指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了该怎么处理)
 /***************************************************************************/
   ExecutorService p

你可能感兴趣的:(JAVA学习之路,java,jvm,开发语言)