美文网首页
Java: 线程,线程池

Java: 线程,线程池

作者: 宋song一 | 来源:发表于2018-08-19 23:12 被阅读11次

•进程:一个启动的应用程序,是线程的载体.进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程

•线程是系统中最小的执行单元.JVM抢占式调度

•启动应用程序启动进程,进程默认情况下会启动主线程

进程与线程的区别
进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。

一.多线程的创建方式:

1. 继承(extends) Thread

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
  2. 创建Thread子类的实例,即创建了线程对象
  3. 调用线程对象的start()方法来启动该线程
//自定义中断标记,实现指定时间中断
class MyThread extends Thread {
    private boolean needInterrupt = false;
    public void setNeedInterrupt(boolean needInterrupt) {
        this.needInterrupt = needInterrupt;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (needInterrupt) return;
            System.out.println("打印" + i);

            try {
                sleep(1000L);

            } catch (InterruptedException e) {
                System.out.println("报错" + e.getMessage());
                e.printStackTrace();
            }
        }
    }}
public class Main {
    public static void main(String[] args) {

        MyThread thread = new MyThread();
        thread.setName("ccc");
       // thread.setPriority(10);             设置线程优先级
        thread.start();

        try {
            thread.sleep(3000L);
            thread.interrupt();
           thread.setNeedInterrupt(true);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }}}

2.实现(implements) Runnable

  1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
  3. 调用线程对象的start()方法来启动线程。
    • Runnable不返回结果,也不能抛出被检查的异常
//多线程卖100张票
public class MyRunnable implements Runnable {
    private int Ticket = 100;

    @Override
    public void run() {
        while (true) {
            synchronized (MyRunnable.class) { //用this也可
                if (Ticket > 0) {
                    System.out.println("卖出第" + (101 - Ticket) + "张票");
                    Ticket--;
                } else return;
            }

        }
    }
}

public class Main {
    public static void main(String[] args) {
  MyRunnable runnable = new MyRunnable();
        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        Thread thread3 = new Thread(runnable);
        thread1.setName("窗口1");
        thread1.setName("窗口2");
        thread1.setName("窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}      

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runnable接口的话,则很容易实现资源共享

实现Runnable接口比继承Thread类所具有的优势

  1. 适合多个相同的程序代码的线程去共享同一个资源。
  2. 可以避免java中的单继承的局限性。
  3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
  4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类

2.1 匿名内部类创建

使用线程的匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
使用匿名内部类的方式实现Runnable接口,重写Runnable接口中的run方法:

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("美美" + i);
                }
            }
        },"hello");
        new Thread(thread).start();
        System.out.println(thread.getName());

        Thread.currentThread().setName("美眉");   //设置main线程名字
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+":妹"+i);
        }

    }
}

3.Callable

有返回值(可选),可以抛出异常

import java.util.concurrent.Callable;

public class MyCallAble implements Callable<String> {

    @Override
    public String call() throws Exception {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
            
        }
        return "callable返回了数据";
    }
}

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main2 {
    public static void main(String[] args) {
        MyCallAble callAble = new MyCallAble();
        ExecutorService service = Executors.newFixedThreadPool(5);
        Future<String> submit = service.submit(callAble);
        try {
            System.out.println(submit.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

二、线程安全

线程安全问题都是由全局变量及静态变量引起的。

若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全

1.同步代码块 synchronized

部分代码需要加锁

synchronized(同步锁){
              需要同步操作的代码
}

同步锁:

  1. 锁对象 可以是任意类型。
  2. 多个线程对象 要使用同一把锁。(需要保证唯一)

2.同步方法

修饰符 synchronized 返回值类型 方法名(){
           可能会产生线程安全问题的代码
}

同步锁:
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)

3.锁机制(同步锁/lock锁)

Lock lock = new ReentrantLock();
lock.lock();

采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,

使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生

Lock lock = ...;

lock.lock();

try{

    //处理任务

}catch(Exception ex){

}finally{

    lock.unlock();   //释放锁
}

三.线程状态

NEW(新建)

线程刚被创建,但是并未启动。还没调用start方法。
Runnable (可运行)
线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操
作系统处理器。
Blocked(锁阻塞)
当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状
态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting(无限等待)
一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个
状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
TimedWaiting(计时等待)
同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态
将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、
Object.wait。
Teminated(被终止)
因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡

四.线程通信(等待唤醒机制)

4.1. 2个线程间使用flag变量实现交替执行

//实现2个线程间交替执行
public class Demo {
    int flag = 1;


    public synchronized void method1() {
        if (flag != 1) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print("学");
        System.out.print("习");
        System.out.print("了");
        System.out.print("吗");
        System.out.println();
        this.flag = 2;
        this.notify();

    }

    public synchronized void method2() {
        if (flag != 2) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print("s");
        System.out.print("t");
        System.out.print("u");
        System.out.print("d");
        System.out.print("y");
        System.out.println();
        this.flag = 1;
        this.notify();
    }
}

public class Main {

    public static void main(String[] args) {
        Demo demo = new Demo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    demo.method1();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    demo.method2();
                }
            }
        }).start();
    }
}

4.2 替换if为 whie可实现3个线程间有效通信(交替执行)

4.3 生产者消费者案例

//生产者
public class Producter extends Thread {
    BaoZi bz;

    @Override
    public void run() {
        int num = 0;
        while (true) {
            synchronized (bz) {
                if (bz.flag == true) {
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("生产者唤醒,开始生产");
                if (num % 2 == 0) {
                    bz.pier = "面皮";
                    bz.xianer = "猪肉大葱";

                } else {
                    bz.xianer = "黑芝麻";
                    bz.pier = "米皮";
                }
System.out.println("包子皮是:" + bz.pier + ";包子馅是:" + bz.xianer);
                num++;
                bz.flag = true;
                bz.notify();
            }
        }
    }
}

//消费者
public class Eater extends Thread {
    BaoZi bz;

    @Override
    public void run() {
        while (true) {
            synchronized (bz) {
                if (bz.flag == false) {
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
System.out.println("吃货开始吃包子,皮是:" + bz.pier + ";馅是:" + bz.xianer);
                bz.flag = false;
                bz.notify();
                System.out.println();
            }
        }
    }
}


public class BaoZi {
    boolean flag = false;
    String pier;
    String xianer;
}


public class Main1 {
    public static void main(String[] args) {

        BaoZi baoZi = new BaoZi();

        Producter pt = new Producter();
        pt.bz = baoZi;
        pt.start();
        Eater eater = new Eater();
        eater.bz = baoZi;
        eater.start();
    }
}

五.线程池 ExecutorService(interface)

5.1 合理使用的好处:

  1. 降低资源消耗。
  2. 提高响应速度。
  3. 提高线程的可管理性

官方建议使用Executors工程类来创建线程池对象

5.2 使用步骤

  1. 创建线程池对象。

  2. 创建Runnable接口子类对象。(task)

  3. 提交Runnable接口子类对象。(take task)

    execute 无返回值

    submit 有返回值

  4. 关闭线程池(一般不做) shutdown

    public class MyRunnable implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(i+" "+Thread.currentThread().getName());
                try {
                    Thread.sleep(10L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public class Main {
        public static void main(String[] args) {
            ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);
       //Runtime.getRuntime().availableProcessors()获取当前cpu核心数
            MyRunnable runnable = new MyRunnable();
            service.execute(runnable);
            //execute 无返回值
            Future<?> submit = service.submit(runnable);
            //submit 有返回值
            
        }
    }
    
    
    

六.线程 join

​ •将线程之间的并行执行变为串行执行

七.守护线程 setDaemon

•守护线程是指在程序运行的时候在后台提供一种通用的服务的线程(垃圾回收线程)

•主线程执行结束,守护线程就结束

守护线程设置在线程启动之前.参数为true表示是daemon thread.

thread2.setDaemon(true); 
thread2.start();

相关文章

网友评论

      本文标题:Java: 线程,线程池

      本文链接:https://www.haomeiwen.com/subject/egbhiftx.html