java 线程基础知识

JAVA线程基础

    每一个Java程序都至少有一个线程--主线程。当一个Java程序启动时,JVM会自动创建主线程,并在该线程中调用程序的main()方法
    JVM 还会创建了其他线程,即使我们看不到,如,与垃圾回收,对象终止和其他JVM内务处理相关的线程。
    其他工具也创建线程,如AWT(抽象窗口工具箱[Abstract Windowing Toolkit])或Swing UI工具箱,servlet容器,应用程序服务器和RMI(远程方法调用[Remote Method Invocation])。
    使用线程的一些原因:
     1.使UI响应更快
     2.利用多处理器系统
     3.简化建模
     4.执行异步或后台处理

线程生命

  • 创建线程

    每个Java程序至少包含一个线程:主线程。其他线程可以通过实例化Thread对象或实例化继承Thread的对象来创建其他线程。

  • 启动线程

    Thread线程在调用start()之前,就已经存在,并且退出之后仍然存在。这样可以控制或获取关于以创建的、线程的信息,即使线程还没有启动或已经完成了

  • 结束线程

    • 线程到达其run()方法的末尾
    • 线程抛出一个未捕获到的Exception或Error
    • 另一个线程调用一个弃用的stop()方法。
  • 加入线程

    Thread API 包含了等待另一个线程完成的方法:join()方法。当调用Thread.join()时,调用线程将阻塞,直到目标线程完成为止
    Thread.join()通常由使用线程的程序使用,以将大问题划分成许多小问题,每个小问题分配一个线程。

  • 调度

    永远不要假设一个线程会在另一个线程之前执行某些操作,除非已经使用了同步以强制一个特定的执行顺序。

public class ThreadTest2
{
    static class Thread1 extends Thread
    {
        @Override
        public void run(){
            System.out.println("A");
            System.out.println("B");
        }
    }
    static class Thread2 extends Thread
    {
        @Override
        public void run(){
            System.out.println("1");
            System.out.println("2");
        }
    }
    public static void main(String[] args)
    {
        System.out.println("启动两个线程!");
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        thread2.start();
        while(thread1.isAlive() || thread2.isAlive());
        System.out.println("结束!");
    }
}

可能的结果
- A12B
- A1B2
- AB12
- 12AB
- 1A2B
- 1AB2

  • 休眠

    Thread API 包含了一个sleep()方法,它将使当前线程进入等待状态,直到过了一段指定时间,或者直到另一个对当前线程的Thread对象调用了Thread.interrupt(),从而中断了线程。当过了指定时间后,线程又将变为可运行状态。
    Thread.yield()方法就像Thread.sleep()一样,但是它不引起休眠,而是暂停当前线程片刻,这样其他线程可以运行。

  • 守护程序线程

    当且仅当创建线程是守护线程时,新线程才是守护程序。
    Java程序在它的所有非守护线程完成之后退出。

线程字段

类型字段描述
static intMAX_PRIORITY线程可以具有的最高优先级。
static intMIN_PRIORITY线程可以具有的最低优先级。
static intNORM_PRIORITY分配给线程的默认优先级。
longid线程标识符(getter)
Stringname线程名字(setter和getter)
intpriority线程优先级(setter和getter)
Thread.Statestate线程状态(getter)
booleanalive是否处于活动状态(is方法)
booleandaemon是否为守护线程(setter,is方法)
booleaninterruptted是否被中断(is方法)

线程方法

构造方法描述
Thread()
Thread(String name)定义线程的名字
Thread(Runnable target)使用Runnale接口创建线程
静态方法描述
currentThread():Thread返回对当前正在执行的线程对象的引用。
sleep(long millis)休眠millis毫秒
yield()暂停当前正在执行的线程对象,并执行其他线程。
interrupted()测试当前线程是否已经中断,该方法会清除线程的 中断状态
实例方法描述
start()线程开始执行
run()Thread子类重写run(),实现Runnable类要实现该方法
join()等待线程的执行结束
toString()返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
interrupt()中断线程,
    在一个循环中调用sleep(),应该将循环放到try-catch之中,如果将try-catch放到循环中,则即使线程被中断,仍然可能继续执行

线程状态

这里写图片描述

    isAlive():判断线程状态的方法,如果线程处于就绪,阻塞,运行状态,则返回true,如果线程处于新建并且没有启动的状态,或者已经结束,则返回false

    interrupt():当线程处于就绪状态或运行状态时,给它设置一个中断标志;当线程处于阻塞状态时,它将被唤醒并进入就绪状态,同时抛出异常java.lang.InterruptedException。

共享对数据的访问

存在于一个内存空间的所有线程

    线程与同一个进程中的其他线程共享相同的进程上下文,包括内存。只要访问共享变量(静态或实例字段),线程就可以方便的互相交换数据,但线程还必须确保它们以受控的方式访问共享变量,以避免它们互相干扰对方的更改。

受控访问的同步

    为了确保可以在线程之间以受控方式访问数据,Java语言提供了两个关键字:synchronized和volatile
    synchronized:确保了一次只有一个线程可以执行代码的受保护部分(互斥,mutual exclusion 或者说mutex),而且它确保了一个线程更改的数据对于其他线程是可以见的。
    volatile,只适合控制对基本变量(整数,布尔变量等)的当个实例的访问。当一个变量被声明成volatile,任何对该变量的写操作都会绕过高速缓存,直接写入主内存,而任何对该变量的读取也都会绕过高速缓存,直接从主内存中取。这表示所有线程在任何时候看到的volatile变量值都相同。

用锁保护原子代码块

    同步使用监控器(monitor)或锁的概念,以协调对特定代码块的访问。
    每个Java对象都一个相关的锁。同一时间只能有一个线程持有Java锁。当线程进入synchronized代码块时,线程会阻塞并等待,直到锁可用,当它可用时,就会获得这个锁,然后执行代码块。当控制退出受保护的代码块时,即到达了代码块末尾或者抛出了没有在synchronized块中捕获异常时,就会释放锁。
    Java锁定合并了一种互斥形式。每次只有一个线程可以持有锁。锁用于保护代码块或整个方法,必须记住是锁的身份保护了代码块,而不是代码块本身,这一点很重要。一个锁可以保护许多代码块或方法。
    仅仅因为代码块由锁保护并不表示两个线程不能同时执行该代码块。只表示如果两个线程正在等待相同的锁,则它们不能同时执行该代码。

    对于普通的synchronized方法,这个锁是一个对象,将针对它调用方法;对于静态synchronized方法,这个锁是与Class对象相关的监控器,在该对象中声明了方法。
    由于同步防止了多个线程同时执行一个代码块,因此性能上就有问题,即使在单处理器系统上,最好在尽可能最小的需要保护的代码块上使用同步。

    加锁同步(显示加锁)。一个锁是一个Lock接口的实例,定义了加锁和释放锁的方法。锁也可以使用newCondition()方法来创建任意个数的condition对象用来进行线程通信。
    java.util.concurrent.locks.*;
    接口:Lock
        lock()
        unlock()
        newCondition():Condition
    实现类:ReentrantLock
        ReentrantLock()
        ReentrantLock(fair:boolean)
            创建具有公平策略的锁
    ReentrantLock是为创建相互排斥的锁的Lock的具体实现。公平策略:将锁给等待时间最长的线程。否则随机。

    线程间协作,通过保证在临界区上多个线程的互斥,线程同步完全可以避免竞争状态的发生,但是有时候却需要线程间协作(消费者和生产者)。
    通过调用Lock对象的newCondition()方法创建条件,然后通过await(),signal()和signalAll来实现线程间通信
    java.util.concurrent.*;
    类:Condition
    await()
    signal()
    signalAll():Condition

wait()、notify() 和notifyAll() 方法

    需要与synchronized一起使用,因此直接学会用synchronized就可以了
    Object类还包括一些方法,可以让线程相互通知事件的发生
    Object类定义了wait(),notify()和notifyAll()方法,要执行这些方法,必须拥有相关对象的锁。
    wait()会让调用线程休眠,直到用Thread.interrupt()中断它,过了指定时间,或者另一个线程用notify()或notifyAll()唤醒它。
    当对某个对象调用notify()时,如果有任何线程正在通过wait()等待该对象,那么就会唤醒其中一个线程。当对某个对象调用notifyAll()时,会唤醒所有正在等待该对象的线程。
    这些方法时更复杂的锁定,排队,和并发性代码的构件。但是,notify()和notifyAll()的使用很复杂,尤其是,使用notify()来替代notifyAll()是有风险的。除非你确实知道正在做什么,否则就使用notifyAll()。
    可以使用util.concurrent包,这是一个被广泛使用的开源工具箱,里面都是有用的并发性实用程序。
public class SynchronizedTest
{
    //将一个对象设置为一把锁。
    private static Object lockObject = new Object();
    private static int x , y;

    private static class Thread1 extends Thread{
        @Override
        public void run(){
            synchronized(lockObject){
                x = y = 0;
                System.out.println(x);
            }
        }
    }
    private static class Thread2 extends Thread{
        @Override
        public void run(){
            synchronized(lockObject){
                x = y = 1;
                System.out.println(y);
            }
        }
    }

    public static void main(String[] args)
    {
        Thread1  thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        thread2.start();

        System.out.println("Hello World!");
    }
}

线程池

    管理并发执行任务个数的理想方法
    Java提供了Executor接口来执行线程池中的任务,提供ExecutorService接口来管理和控制任务。ExecutorService是Executor的子接口。
    java.util.concurrent.*;
    类:Executors
        静态方法
        newFixedThreadPool(numberOfThread):ExecutorService
            固定数目的线程池
        newCachedThreadPool():ExecutorService
            线程池按需创建线程
    接口:ExecutorService,继承Executor
        shutdown()
            关闭,不再接受新任务
        shutdownNow():List<Runnable>
            立即关闭,并结束所有任务,返回未完成任务
        isShutdown()
        isTerminated()
    接口:Executor
        execute(Runnable object)

信号量

    信号量可以用来限制访问共享资源的线程数。在访问资源之前,线程必须从信号量获取许可;在访问资源之后,这个线程必须将许可返回给信号量。
    java.util.concurrent
    类:Semaphore
    Semaphore(numberOfPermits:int)
    Semaphore(numberOfPermits:int,fair:boolean)
    acquire()   :获取信号量许可。如果无许可可用,线程就被锁住知道有许可可用
    release()   :释放一个许可
    只有一个许可的信号量可以用来表示互斥的锁。

用例

1、使用一个线程用于计时,并使用另一个线程完成工作
实例程序的功能是:使用两个线程,一个用于计时,一个用于执行实际工作,主线程使用非常简单的的算法计算素数;另一个线程用于计时十秒钟。

public class MainThread extends Thread
{
    private static final long MAX_PRIMES = 1000000;
    private static final int MAX_SECONDS = 10000;
    private static volatile boolean finished = false;
    private long primes = 0;
    @Override
    public void run(){
        boolean flag = true;
        for(long count = 2; count < MAX_PRIMES; count ++){
            if(finished){
                break;
            }
            //判断是否是素数
            for(int i = 0 ; i < Math.sqrt(count) ; i ++){
                if(count % i == 0){
                    flag = false;
                    break;
                }
            }
            if(flag){
                primes++;
                System.out.println("素数个数:" + primes);
            }
            flag = true;
        }
    }

    public static void main(String[] args)
    {
        MainThread mainThread = new MainThread();
        mainThread.start();
        try{
            Thread.sleep(MAX_SECONDS);
        }catch(InterruptedException e){
            System.out.println("时间到!");
        }
        finished = true;
        System.out.println("时间到!");
    }
}

2、用多个线程分解大任务
计算1000000之内的素数个数

public class CountLargePrimes
{
    static class SmallPlan extends Thread
    {
        private long start;
        private long end;
        private long count;

        //构造函数
        public SmallPlan(long start , long  end){
            super();
            this.start = start;
            this.end = end;
            count = 0;
        }

        public long getCount(){
            return count;
        }
        //计算素数个数
        private void primes(){
            for(long i = start ; i <= end ; i ++){
                if(isPrime(i)){
                    count++;
                }
            }
        }
        //判断是否是素数
        private boolean isPrime(long num){
            for(long i = 2; i <= Math.sqrt(num); i++){
                if(num % i == 0){
                    return false;
                }
            }
            return true;
        }

        @Override
        public void run(){
            primes();
        }
    }

    public static void main(String[] args)
    {
        System.out.println("开启九个任务!");
        long startTime = System.currentTimeMillis();
        long count = threadCount();
        long endTime = System.currentTimeMillis();
        System.out.println("1000000里面素数的个数是:" + count);
        System.out.println("耗时:" + (endTime - startTime));
    }

    public static long threadCount(){
        //78498个,1329ms
        final long M = 100000;
        SmallPlan[] threads = new SmallPlan[10];
        long start = 2;
        long end = M;
        long count = 0;
        threads[0] = new SmallPlan(start,end);
        threads[0].start();

        for(int i = 1; i <= 9 ; i++){
            start = i*M+1;
            end = (i+1)*M;
            threads[i] = new SmallPlan(start,end);
            threads[i].start();
        }
        for(int i = 0 ; i < 10 ; i ++){
            //等待十个进程结束,然后汇总结果
            try{
                threads[i].join();
            }catch(InterruptedException ex){
            }
            count += threads[i].getCount();
        }
        return count;
    }

    public static long FunCount(){
        //78498个,2514ms
        long  count = 0;
        //计算素数个数
        for(long i = 2 ; i <= 1000000; i++){
            if(isPrime(i)){
                count++;
            }
        }
        //判断是否是素数
        return count;
    }
    private static boolean isPrime(long num){
        for(long i = 2; i <= Math.sqrt(num); i++){
            if(num % i == 0){
                return false;
            }
        }
        return true;
    }
}

3、生产者和消费者

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;

public class ProducerAndConsumer
{
    //创建缓冲区
    private static Buffer buffer = new Buffer();

    public static void main(String[] args)
    {
        System.out.println("Hello World!");
        Thread producer = new Thread(new Producer());
        Thread consumer = new Thread(new Consumer());
        producer.start();
        consumer.start();
    }

    //消费者
    static class Consumer implements Runnable
    {
        public void run(){
            try{
                while(true){
                    buffer.poll();
                    Thread.sleep((int)(Math.random()*1000));
                }
            }catch(InterruptedException ex){}
        }
    }
    //生产者
    static class Producer implements Runnable
    {
        public void run(){
            try{
                while(true){
                    buffer.push(new Random().nextInt(20));
                    Thread.sleep((int)(Math.random()*1000));
                }
            }catch(InterruptedException ex){}
        }
    }
}

//缓冲池
class Buffer
{
    //创建锁
    private static Lock lock = new ReentrantLock();
    //条件1---缓冲池空
    private static Condition empty = lock.newCondition();
    //条件2---缓冲池满
    private static Condition full = lock.newCondition();
    //缓冲池大小
    private static final int  size =  10;
    //缓冲池
    private Queue<Integer> queue = new LinkedList<Integer>();

    //取数
    public void poll(){
        lock.lock();
        try{
            while(queue.size() == 0){
                System.out.println("\t\t\t\tWait for producer:");
                empty.await();
            }
            int num = queue.poll();
            System.out.println("\t\t\t\t["+queue.size()+"]poll:" + num);
            full.signal();
        }catch(InterruptedException ex){

        }finally{
            lock.unlock();
        }

    }
    //存数
    public void push(int num){
        lock.lock();
        try{
            while(queue.size() == 10){
                System.out.println("Wait for consumer:");
                full.await();
            }
            queue.offer(num);
            System.out.println("["+queue.size()+"]push:" + num);
            empty.signal();
        }catch(InterruptedException ex){

        }finally{
            lock.unlock();
        }
    }
}
  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值