Java并发编程之美之并发编程线程基础

什么是线程

  进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,一个进程至少有一个线程进程的多个线程共享进程的资源

  java启动main函数其实就是启动了一个JVM的进程,而main函数所在的线程就是这个进程的一个线程,也称主线程。

进程和线程关系

  

  一个进程有多个线程,多个线程共享进程的堆和方法区资源,但是每个线程有自己的程序计数器栈区域

  程序计数器是一块内存区域用来记录线程当前要执行的指令地址。如果执行的是native方法,那么pc计数器记录的是undefined地址,只有执行java代码时pc计数器记录的才是下一条指令的地址;

  进程的栈资源存储该线程的局部变量,局部变量是该线程私有的,其它线程访问不了,除此之外栈还用来存放线程的调用栈帧

  是进程中最大的一块内存,堆是被进程中的所有线程共享的,是进程创建时分配的,堆里面存放使用new操作创建的对象实例。

  方法区则用来存放JVM加载的类,常量及静态变量等信息,也是线程共享的

线程创建和运行

  创建方式:继承Thread类    实现Runnable接口,使用FutureTask方式

    1.继承Thread类

public class ThreadTest {
     public static class MyThread extends Thread{
        @Override
       public void run(){
            System.out.println(" I am a child thread!") ;
        }        

     public static void main(String[] args){
          MyThread  t = new MyThread();
          t.start();
        }
     }
}

    2.实现Runnable接口

public class RunnableTest {

    public static class MyRunnable implements Runnable
    {
        @Override
        public void run() {
            System.out.println("I am a child thread!");
        }
    }

    public static void main(String[] args) {
        new Thread(new MyRunnable()).start();
    }
} 

3.使用FutureTask

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class CallableTest implements Callable<Boolean>{

    @Override
    public Boolean call() throws Exception {

        return true;
    }

    public static void main(String[] args) {
        FutureTask<Boolean> f = new FutureTask<>(new CallableTest());
        new Thread(f).start();
        try{
            Boolean  r = f.get();
            System.out.println(r);
        }catch(Exception e)
        {
            e.printStackTrace();
        }
    }
}

 小结:

  使用继承方式的好处是方便传参,你可以在子类里面添加成员变量,通过set方法设置参数或者构造函数传递参数,而如果使用Runnable方式,则只能使用主线程里面被声明为final的变量。不好的地方是Java不支持多继承,如果继承了Thread,那么子类不能再继承其它类,而Runnale则没有这个限制。前两种方式都没办法拿到任务的返回值,但是Futuretask方式可以。

线程通知与等待

  java中的Object类是所有类的父类,鉴于继承机制,java把所有类都需要的方法放到了Object类里面,其中就包含通知与等待系列函数。

wait()函数

    当一个函数调用共享变量的wait()方法时,该线程会被阻塞挂起,直到发生下面几件事情之一才返回:1)其它线程调用了该共享对象的notify()或者notifyAll()方法;2)其它线程调用了该线程的interrupt()方法,该线程抛出InterruptedException异常返回;

    如果调用wait()方法的线程事先没有获取该对象的监视器锁,会抛出IllegalMonitorStateException异常;

    获取共享变量的监视器锁

      synchronized(共享变量){doSomething}

      synchronized  void add(int a,int b){doSomething}

   虚假唤醒

      一个线程可以从挂起状态变为可以运行状态(也就是被唤醒),即时该线程没有被其它线程调用notify(),notifyAll()方法进行通知,或者被中断,或者等待超时,这就是所谓的虚假唤醒。

生产者和消费者

/**
 * 容器
 * @author pc
 *
 */
public class Box {

    public final int MAX_SIZE = 10;

    public LinkedList<Integer>   box = new  LinkedList<>();

    public void add() throws Exception
    {
        while(true)
        {
            synchronized(box)
            {
                while(box.size() == MAX_SIZE)
                {
                    box.wait();
                }
                box.push(1);
                System.out.println("add ele and notifyall:"+box.size());
                box.notifyAll();
            }
        }
    }

    public void remove() throws Exception
    {
        while(true)
        {
            synchronized(box)
            {
                while(box.size() == 0)
                {
                    box.wait();
                }

                box.pop();
                System.out.println("remove ele and notifyall:"+box.size());
                box.notifyAll();
            }
        }
    }
}
/**
 * 生产者和消费者
 * @author pc
 *
 */
public class Program {

    public Box box;

    public Program(Box box) {
        super();
        this.box = box;
    }

    public  class Consumer implements Runnable
    {
        @Override
        public void run() {
            try {
                box.remove();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public class Producer implements Runnable
    {

        @Override
        public void run() {
            try {
                box.add();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Program p = new Program(new Box());
        Program.Consumer c = p.new Consumer();
        Program.Producer pd = p.new Producer();

        Thread t1 = new Thread(c);
        Thread t2 = new Thread(pd);
        t1.start();
        t2.start();
    }
}

 当前线程调用共享变量的wait()方法后只会释放当前共享变量上的锁,如果当前线程还持有其它共享变量的锁,则这些锁时不会被释放的。

public class Test {

    private static  volatile Object resourceA = new Object();
    private static  volatile Object resourceB = new Object();

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t1 try  get resourceA lock...");
                synchronized(resourceA)
                {
                    System.out.println("t1 get resourceA lock...");
                    System.out.println("t1 try get resourceB lock...");
                    synchronized(resourceB)
                    {
                        System.out.println("t1  get resourceB lock...");
                        System.out.println("t1 unlock resourceA...");
                        try {
                            resourceA.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000L);//等待1s保证t1将resourceA释放出来
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2 try  get resourceA lock...");
                synchronized(resourceA)
                {
                    System.out.println("t2 get resourceA lock...");
                    System.out.println("t2 try get resourceB lock...");
                    synchronized(resourceB)
                    {
                        System.out.println("t2  get resourceB lock...");
                        System.out.println("t2 unlock resourceA...");
                        try {
                            resourceA.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("main thread is over");
    }
}

当一个线程调用共享对象的wait()方法被阻塞挂起后,如果其它线程中断了该线程,则该线程会抛出InterruptedException异常并返回   

public class InterruptedExceptionTest {

    private static volatile Object resouce = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(resouce)
                {
                    try {
                        resouce.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        t1.start();
        System.out.println(" start interupt==");
        t1.interrupt();
        System.out.println(" end interupt==");
        System.out.println("main is over");
    }
}

 wait(long timeout)函数

  该方法相比wait()函数多了一个超时参数,如果线程调用共享变量过程中,没有在指定的timeout时间内被其它线程调用该共享变量的notify()或者notifyAll()方法唤醒,那么该函数还是会因为超时返回

  timeout等于0就相当于wait()函数

  timeout小于0会报错IllegalArgumentException异常

public class WaitTimeOutTest {

    private static Object resource = new Object();

    public static void main(String[] args)throws Exception {
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                synchronized(resource)
                {
                    System.out.println("start wait()"+System.currentTimeMillis()/1000);
                    try {
                        resource.wait(1000L);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println("end   wait()"+System.currentTimeMillis()/1000);
                }
            }
        });

        t1.start();
        t1.join();
    }

wait(long timeout,int nanos)

  内部调用的时wait(long timeout),只有在nanos>0时才使参数timeout递增1;

notify()函数

  一个线程调用共享变量的notify()方法后,会唤醒一个在该共享变量上调用wait系列方法后被挂起的线程。一个共享变量上可能会有多个线程在等待,具体唤醒哪个等待的线程时随机的。

  被唤醒的线程不会立即返回,需要先获取共享变量的锁

  类似wait系列方法,只有当前线程获取到共享变量的监视器锁之后,才可以调用共享变量的notify()方法,否则会抛出IllegalMonitorStateException异常。

notifyAll()函数

  唤醒所有在该共享变量上由于调用wait系列方法而被挂起的线程

  如果调用notifyAll()之后有线程调用了wait()方法,则该线程不能被唤醒。

public class NotifyTest {

    private static Object resource = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(resource)
                {
                    System.out.println("t1  start wait()");
                    try {
                        resource.wait();
                        System.out.println("t1 end wait()");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(resource)
                {
                    System.out.println("t2  start wait()");
                    try {
                        resource.wait();
                        System.out.println("t2 end wait()");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }//等待1s
                synchronized(resource)
                {
                    System.out.println("t3 start notify()");
                    resource.notify();            //resource.notifyAll();
                }
            }
        });

        t1.start();
        t2.start();
        t3.start();

        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 等待线程执行终止的join方法

ublic class JoinTest {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 end");
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2 end");
            }
        });

        t1.start();t2.start();
        System.out.println("wait t1,t2 over");
        t1.join();t2.join();
    }
}

让线程睡眠的sleep方法

  Thread类中的一个静态sleep方法,当一个执行中的线程调用Thread的sleep方法后,调用线程会暂时让出指定时间的使用权,也就是不参数与CPU调度,但是该线程拥有的监视器资源,比如锁还是持有不让出的。指定时间结束后该函数会正常返回。

   如果在睡眠期间调用了该线程的interrupt()方法中断了该线程,则线程会在调用sleep方法的地方抛出InterruptedException异常而返回。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SleepTest {

    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try
                {
                    lock.lock();
                    System.out.println("t1 start sleep");
                    Thread.sleep(1000);
                    System.out.println("t1 end sleep");
                }catch(Exception e)
                {
                    e.printStackTrace();
                }finally
                {
                    lock.unlock();
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try
                {
                    lock.lock();
                    System.out.println("t2 start sleep");
                    Thread.sleep(1000);
                    System.out.println("t2 end sleep");
                }catch(Exception e)
                {
                    e.printStackTrace();
                }finally
                {
                    lock.unlock();
                }
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

输出结果一直是t1或t2结束其中之一结束之后才开始执行下一线程,证明线程睡眠期间不会释放锁;

让出CPU执行权的yield方法

  Thread类中的静态yield方法,当一个线程调用yield方法时,其实就是在暗示线程调度器当前线程请求让出自己的CPU使用,但是线程调度器可以无条件忽略这个暗示

  当一个线程调用yield方法时,当前线程会让出CPU使用权,然后处于就绪状态,线程调度器会从线程就绪队列里面获取一个线程优先级高的线程,当然也有可能调度到刚刚让出CPU的线程

线程中断

  java中线程中断是一种线程间的协作模式,通过设置线程的中断标志并不能直接终止该线程的执行,而是被中断的线程根据中断状态自行处理

  void interrupt():中断线程

  boolean isInterrupted():检测当前线程是否被中断

  boolean interrupted():检测当前线程是否被中断,中断返回true,并清除中断标志

线程死锁

  死锁是指两个或两个以上的线程执行过程中,因争夺资源而做成的互相等待的现象。无外力作用的情况下,这些线程会一直互相等待下去而无法继续运行下去

  

死锁出现的条件

  1)互斥条件:线程堆已经获取的资源进行排他性使用,即该资源同时只能有一条线程占用。如果此时还有其它线程请求获取该资源,请求者只能等待,直至占有的线程释放该资源;

  2)请求并持有条件:指一个线程已经持有了至少一个资源,但是又提出了新的资源请求,而新资源被其它线程占有,所以当前线程会被阻塞,但阻塞的同事并不释放自己已经获取的资源。

  3)不可剥夺条件:指线程获取到的资源在自己使用完之前不能被其它线程抢占,只有在自己使用完毕后才由自己释放资源

  4)环路等待条件:发生死锁时,必然存在一个线程-资源环形链,即线程集合{T0,T1,T2,...Tn}中T0等待T1的资源,T1等待T2的资源,...Tn正在等待T0占用的资源

public class DeadThreadTest {

    private volatile static Object resourceA = new Object();
    private volatile static Object resourceB = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {

                System.out.println("t1 try get resourceA lock...");
                synchronized(resourceA)
                {
                    System.out.println("t1  get resourceA lock...");
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("t1 try get resourceB lock...");
                    synchronized(resourceB)
                    {
                        System.out.println("t1 get resourceB lock...");
                    }
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t2 try get resourceB lock...");
                synchronized(resourceB)
                {
                    System.out.println("t2  get resourceB lock...");
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("t2 try get resourceA lock...");
                    synchronized(resourceA)
                    {
                        System.out.println("t2 get resourceA lock...");
                    }
                }
            }
        });

        t1.start();
        t2.start();
    }
}

输出:  

t2 try get resourceB lock...
t1 try get resourceA lock...
t1 get resourceA lock...
t2 get resourceB lock...
t1 try get resourceB lock...
t2 try get resourceA lock...

 

如何避免死锁的发生

  1)破坏掉至少一个构造死锁的必要条件即可

  2)使用资源的有序性原则

守护线程与用户线程

  线程分类:守护线程【daemon】,用户线程【user】

  main函数就是用户线程

  当最后一个用户线程结束时,JVM会正常退出,只要有一个用户线程没有结束,正常情况下JVM就不会退出

  

public class DamonThreadTest {

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                for(;;)
                {

                }
            }
        });

        t1.setDaemon(false);
        t1.start();
        System.out.println("main is over");
    }
 

ThreadLocal

  多线程访问同一共享变量时特别容易出现并发问题,特别时在多个线程需要对一个共享变量进行写入时,为了保证线程安全,一般使用者在访问共享变量时需要进行适当的同步

  同步措施一般是加锁,这就需要使用者对锁有一定的了解

  ThreadLocal提供了线程本地变量,也就是如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。

public class ThreadLocalTest {

    private static ThreadLocal<String> local = new ThreadLocal<>();

    public static void print(String name)
    {
        System.out.println(name+"--"+local.get());
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new  Runnable() {

            @Override
            public void run() {
                local.set("1234");
                print(Thread.currentThread().getName());
            }
        },"t1");
        Thread t2 = new Thread(new  Runnable() {

            @Override
            public void run() {
                local.set("5678");
                print(Thread.currentThread().getName());
            }
        },"t2");

        t1.start();
        t2.start();
    }
}

output:  

t1--1234
t2--5678

 

 线程上下文切换

  多线程编程中,线程个数一般都大于CPU个数,而每个CPU同一时刻只能被一个线程使用,为了让用户感觉多个线程在同时运行,CPU采用采用了时间片轮转的策略,也就是给每个线程分配一个时间片,线程在时间片内占用CPU执行任务,

当前线程使用完时间片后,就会处于就绪状态并让出CPU让其它线程占用,这就是上下文切换。

切换时机:1)当前线程使用完CPU分配的时间片处于就绪状态  2)当前线程被其它线程中断

原文地址:https://www.cnblogs.com/lpfalpd/p/10786216.html

时间: 2024-08-07 21:19:06

Java并发编程之美之并发编程线程基础的相关文章

[Java并发编程之美]第1章 线程基础(待更新)

第1章 线程 线程与进程 进程是操作系统资源分配和调度的基本单位,但cpu资源是分配到线程的,也就是线程是CPU分配的基本单位. 线程自己的栈资源中,存放的局部变量是线程私有的,其他线程无法访问,除此之外栈还存线程的调用栈帧. 线程创建 三种方式:实现Runnable接口的run方法:继承Thread类并重写run方法:使用FutureTask方式. 线程等待与通知 1 wait() 线程先要事先获得共享变量上的监视器锁,然后当一个线程调用一个共享变量的wait()方法,该线程会被阻塞挂起,并且

Windows核心编程读书笔记-第六章线程基础

1.相较于线程,进程所使用的系统资源更多.其原因在于地址空间.为一个进程创建一个虚拟的地址空间需要大量系统资源.线程只有一个内核对象和一个栈. 2.线程的入口函数 DWORD WINAPI ThreadFunc(PVOID pvParam){ DWORD dwResult = 0; ... return(dwResult); } 线程函数的几点说明 线程函数可以任意命名. 线程函数只有一个参数,而其意义由我们(而非操作系统)来定义.因此,我们不必担心ANSI/Unicode问题. 线程函数必须返

Java 编程之美:并发极速赛车平台出租编程高级篇

借用 Java 并发极速赛车平台出租haozbbs.comQ1446595067 编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了. 相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作的顺序是不可预期的. 并发编程相比 Java 中其他知识点学习起来门槛相对较高,学习起来比较费劲,从而导致很多人望而却步: 而无论是职场面试和高并发高流量的系统的实现却都还离不开并发编程,从而导致能够真正掌握并发编程的人才成为市场比较迫

《Java并发编程之美》(翟陆续著)高清pdf

<Java并发编程之美> 阿里巴巴技术专家力作,用代码说话.用实例验证,并发编程没有这么难!<Java并发编程的艺术>*作者方腾飞老师好评推荐! ? 百度网盘链接: https://pan.baidu.com/s/12oEEeDEO_YofImkpQA1bLA 提取码: pmkh  内容简介  · · · · · · 并发编程相比 Java 中其他知识点的学习门槛较高,从而导致很多人望而却步.但无论是职场面试,还是高并发/ 高流量系统的实现,却都离不开并发编程,于是能够真正掌握并发

java并发编程之美-阅读记录1

1.1什么是线程? 在理解线程之前先要明白什么是进程,因为线程是进程中的一个实体.(线程是不会独立存在的) 进程:是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程中的一个执行路径,一个进程中至少会有一个线程,进程中的多个线程共享进程的资源. 线程:是cpu分配的基本单位. 由上图可看出,一个进程中会有多个线程,多个线程共享堆和方法区,但是每一个线程都会有自己的栈和程序计数器. 为什么要将栈和程序计数器设置为线程私有的呢? 前边说线程是cpu执行的基本单位,而cp

JAVA并发编程艺术 一(并发编程的挑战)

从今天起开始java并发编程艺术的学习,每一章学习完以后再这里记录下内容的重点,做个笔记,加深印象. 并发编程的目的是为了让程序运行的更快,但是,并不是启动更多的线程就能让程序最大限度地并发执行.在进行并发是,如果希望通过多现场执行任务让程序运行得更快,会面临非常多的挑战,比如上下文切换的问题,死锁的问题,以及受限于硬件和软件的资源限制问题,本章会介绍几种并发编程的挑战以及解决方案 1.上下问切换 即使是单核处理器也支持多线程执行代码,cpu通过给每个线程分配cpu时间片来实现这个机制.时间片是

Java并发编程学习笔记(一)线程安全性 1

什么是线程安全性: 要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的和可变的状态的访问."共享"意味着变量可以由多个线程同时访问,而"可变"则意味着变量的值在其生命周期内可以发生变化. 一个对象是否需要线程安全的,取决于他是否被多个线程访问.这指的是在程序中访问对象的方式,而不是对象要实现的功能.要使得对象时线程安全的,需要采用同步机制来协同对对象可变状态的访问.如果无法实现协同,那么可能导致数据破坏以及其他不该出现的结果. 如果当多个线程访

(转)《深入理解java虚拟机》学习笔记10——并发编程(二)

Java的并发编程是依赖虚拟机内存模型的三个特性实现的: (1).原子性(Atomicity): 原子性是指不可再分的最小操作指令,即单条机器指令,原子性操作任意时刻只能有一个线程,因此是线程安全的. Java内存模型中通过read.load.assign.use.store和write这6个操作保证变量的原子性操作. long和double这两个64位长度的数据类型java虚拟机并没有强制规定他们的read.load.store和write操作的原子性,即所谓的非原子性协定,但是目前的各种商业

Java 面试知识点解析(二)——高并发编程篇

前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大部分内容参照自这一篇文章,有一些自己补充的,也算是重新学习一下 Java 吧. 前序文章链接: Java 面试知识点解析(一)--基础知识篇 (一)高并发编程基础知识 这里涉及到一些基础的概念,我重新捧起了一下<实战 Java 高并发程序设计>这一本书,感觉到心潮澎湃,这或许就是笔者叙述功底扎实的