zoukankan      html  css  js  c++  java
  • 基础知识:Java多线程编程

    关于线程和进程

       

    线程是进程的一个执行单元,它和进程一样拥有独立的执行控制,由操作系统负责调度,它们俩的区别可以用一句话概括之,那就是进程是程序的一次执行,而线程可以理解为进程执行的一段程序片段,也就是说它们是一种包含关系,线程不能独立运行,必须依存在进程之中

       

    另外从资源分配的角度上看,进程是系统进行资源分配和调度的一个独立单位,而线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

       

    进程间通信和线程间通信的区别

       

    在通信方面也是,进程间通信比较困难,因为进程之间相互独立,而线程之间共享一块内存区域,通信方便

       

    进程间通信主要包括管道, 系统IPC(包括消息队列,信号量,共享存储), SOCKET.

    而进程间通信主要包括共享内存,比如经典的生产者和消费者通过共享内存的方式进行通信;它们共享的内存是:SyncStack对象;生产者通过SyncStack的同步方法pop向其中添加对象;消费者通过SyncStack的同步方法pop方法在SyncStack对象中获取对象;这是对象间共享内存(或共享数据区域)的方式进行的通信。

       

    零散补充

       

    另外多说一句一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

       

    让一个类成为线程类

       

    让一个类成为线程类有两种方式,一种是实现java.lang.Runnable接口,另外一种是基础java.lang.Thread

       

    Runnable接口和Thread类的区别

       

    它们的区别在于三点,一是Thread类中还提供了额外的方法(其实Thread也是实现了Runnable接口的),二是从继承的角度看,接口比类更灵活,因为只能继承一个类而可以继承多个接口,三是如果类实现Runnable接口,那么当调用这个线程的对象开辟多个线程时,可以让这些线程调用同一个变量,如果类继承Thread的话,那么需要用到内部类来实现类似的功能,原理就是利用内部类可以访问任意外部变量这一特性

       

    如何启动一个线程

       

    通过以上两种方法实现一个线程类之后,线程的实例并没有被创建,所以也没有运行起来,要启动它,就要调用Threadstart方法,注意这里并不是run方法(既不是继承Thread类重写的run方法,也不是实现Runnable接口的run方法,run方法中包含的是线程要干的事儿的主体,跟线程的启动没有关系)

       

    继承Thread的线程类的启动

       

    Thread threadTest=new ThreadTest();

    threadTest.start();

       

    实现Runnable的线程类的启动

       

    Thread thread=new Thread(new RunnableTest());

    thread.start();

       

    synchronized保证线程间同步

       

    如果不用synchronized同步

       

    class MyThread extends Thread {

    public static int index;

    public void run() {

    for (int i = 0; i < 100; i++) {

    System.out.println(getName() + ":" + index++);

    }

    }

    }

       

    public static void main(String[] args) {

    new MyThread().start();

    new MyThread().start();

    new MyThread().start();

       

    }

       

    输出结果

       

    Thread-1:13

    Thread-2:16

    Thread-0:15

    Thread-2:18

    Thread-1:17

    Thread-2:20

    Thread-0:19

       

    synchronized同步

       

    synchronized同步原理

       

    synchronized同步的原理是每个对象都有一个线程锁,synchronized可以用任一对象的线程锁来锁住一段代码,任何想进入该代码段的线程必须在解锁以后才能执行,只有占用该锁资源的线程执行完毕,该锁资源才被释放,其他线程才能进入

       

    class MyThread extends Thread {

    public static int index;

    public static Object        object=new Object();

    public void run() {

    synchronized (object) {

    for (int i = 0; i < 100; i++) {

    System.out.println(getName() + ":" + index++);

    }

    }

    }

    }

       

    另外除了把synchronized加在对象上,也可以加在方法上,这叫做同步方法,其实这时候的锁是加在this所引用的对象上的

       

    生产者消费者模型

       

    首先主类Store,相当于线程要争夺的资源,定义一个最大值,以及一个现有值,构造函数中将最大值作为参数传进去,并且将现有值赋值为0

       

    接下来就是增加和删除两个方法,分别用synchronized标记

       

    在增加方法中,如果满了,则调用thiswait方法,不然的话就现有值增加,然后通知其他线程

       

    在删除方法中,如果空了,则调用thiswait方法,不然的话就现有值减少,然后通知其他线程

       

    public class Store {

    private final int MAX_SIZE;

    private int curCount;

    public Store(int n){

    MAX_SIZE=n;

    curCount=0;

    }

    public synchronized void add(){

    while (curCount>MAX_SIZE) {

    System.out.println("仓库满了");

    try {

    this.wait();

    } catch (InterruptedException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    curCount++;

    System.out.println(Thread.currentThread().toString()+"put"+curCount);

    this.notifyAll();

    }

    public synchronized void remove() {

    while(curCount<0){

    System.out.println("仓库是空的");

    try {

    this.wait();

    } catch (InterruptedException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    System.out.println(Thread.currentThread().toString()+"get"+curCount);

    curCount--;

    this.notify();

    }

    }

       

    然后就是要创建生产者类和消费者类,它们都要是线程类,也就是继承Thread

       

    两个线程类要和公共资源联系起来,就需要在两个类中分别定义一个公共资源的成员变量,然后在构造函数中传入进来赋值

       

    生产者线程类的run方法中为永真循环,不停的添加,添加一个,让线程睡1

       

    消费者线程类的run方法中为永真循环,不停的删除,删除一个,让线程睡1.5

       

    class Producer extends Thread{

    private Store store;

    Producer(Store store){

    this.store=store;

    }

    public void run() {

    while (true) {

    store.add();

    try {

    Thread.sleep(1000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    }

    class Consumer extends Thread{

    private Store store;

    Consumer(Store store)

    {

    this.store=store;

    }

    public void run() {

    while (true) {

    store.remove();

    try {

    Thread.sleep(1500);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    }

       

    然后再在main函数中建立这些线程和启动线程

       

    Store store=new Store(5);

    Thread producer1=new Producer(store);

    Thread producer2=new Producer(store);

    Thread consumer1=new Consumer(store);

    Thread consumer2=new Consumer(store);

    producer1.setName("pro1");

    producer2.setName("pro2");

    consumer1.setName("con1");

    consumer2.setName("con2");

    producer1.start();

    producer2.start();

    consumer1.start();

    consumer2.start();

       

    使用Java线程池

       

    线程池属于对象池,对象池使用的基本思路是:将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用,从而在一定程度上减少频繁创建对象所造成的开销。

       

    并非所有对象都适合拿来池化――因为维护对象池也要造成一定开销。

       

    线程池所对应的类是java.util.concurrent

       

    线程池的构造方法

       

    ThreadPoolExecutor

    (

    int corePoolSize,

    int maximumPoolSize,

    long keepAliveTime,

    TimeUnit unit,

    BlockingQueue<Runnable> workQueue,

    RejectedExecutionHandler handler

       

    )

       

    corePoolSize 线程池维护线程的最少数量 core : 核心)

    maximumPoolSize:线程池维护线程的最大数量

    keepAliveTime 线程池维护线程所允许的空闲时间

    unit 线程池维护线程所允许的空闲时间的单位

    workQueue 线程池所使用的缓冲队列

    handler 线程池对拒绝任务的处理策略

       

    一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。

       

    利用Execute执行器来管理线程对象

       

    不必显式的管理线程的生命周期,可以使用Executors的静态方法newFixedThreadPool 或者newCachedThreadPool来创建ExecutorService对象;

       

    同样也是调用ExecutorService对象的execute方法来添加线程到线程池

       

     

  • 相关阅读:
    在CentOS中配置DNS服务器
    CENTOS5.3 64位 VNC远程控制Centos 安装配置
    Windows Server 2003 FTP服务器配置详解 20091210 14:23:36| 分类: 服务器 | 标签: |字号大
    Centos 5.1 sendmail邮件服务器安装及配置
    DNS安装配置全过程
    tomcat多域名设置
    tomcat6 优化初步
    让CentOS自动备份mysql数据库 不指定
    开通企业邮箱之前,请登录您企业网站的域名注册服务商的域名管理平台,进行DNS配置及指向设置,谢谢您的配合
    18.3.2 在不同主机上使用 RMAN 备份建立物理备用数据库
  • 原文地址:https://www.cnblogs.com/keedor/p/4492255.html
Copyright © 2011-2022 走看看