zoukankan      html  css  js  c++  java
  • 轻松学会多线程(二)——多线程相关概念介绍

    上一篇文章中,我们宏观介绍了多线程的背景,今天我们上一些干货。

    在展开解说多线程之前,我们须要明确进程与线程之间的关系。


    进程与线程

    一个关于进程和线程的比喻非常贴切:一个进程就像是工厂的一个车间。代表CPU所能处理的单个任务。任一时刻,CPU总是执行一个进程,其它进程处于非执行状态。而车间的工人。就好比线程,一个进程能够包括多个线程。

                                                 

                           (线程。就好比工厂里的工人,车间的空间是工人们共享的,多个房间是每一个工人都能够进出的)


    操作系统中。全部执行的任务都相应一个进程。当一个程序进入内存执行。即变成一个进程。比方Hibernate中。一个Session就相应一个进程。用户发出的请求就相应一个进程。请求结束。进程结束。每一个Session都有自己状态;多个用户请求,都会独立处理。所以进程就具有了独立性(拥有自己的独立资源,每一个进程都有自己私有的地址空间)、动态性(差别于静态指令的程序,动态就会有状态)、并发性(并发执行,互不影响)。

    而线程是进程的组成部分。

    线程拥有自己的堆栈、程序计数器和自己的局部变量,但它不拥系统资源。一个进程往往包括多个线程。这多个线程共享父进程的所有资源。

    进程间不共享内存,而线程间共享内存非常easy。

    线程在程序中第独立的。并发的运行流。

    线程共享内存、文件句柄和其它米格进程应有个状态。


    创建线程

    创建线程的方式有多种,能够直接继承Thread类。也能够实现Runnable接口实现多线程。我们能够直接new 一个Thread或者将一个Runnable传入Thread构造函数。

    等等。

    //方法:直接new Thread
    		Thread thread1 = new Thread() {
    			@Override
    			public void run() {
    			}
    		};
    		thread1.start();
    		
    		//方法:将Runnable传入Thread构造函数
    		Thread thread2 = new Thread(new Runnable() {
    			@Override
    			public void run() {
    			}
    		});
    		thread2.start();


    创建的方式有多种。看你的喜好使用。我喜欢使用实现Runnable接口的方式创建。由于这样的方式有非常多优点:

    1、将线程创建与业务逻辑分来,更加面向对象。

    2、Java不支持多继承,假设採用继承Thread类的方式,则不能再继承其它父类。

    3、能够多个线程共享一个target对象。也就是说,这样的方式,很适合多个同样的线程来处理同一份资源的情况,从而能够将CPU、代码和数据分开。形成清晰的模型。体现面向对象思想。


    多线程的问题

    在面向服务架构中,我们使用ESB技术,来完毕系统间交互,致力于解决信息孤岛问题;在面向对象编程中,我们要面向抽象编程,要对公共特征的属性进行抽象。

    则否,面向对象的程度不够,系统就更加无法适应变化。


    而在使用多线程时,多个线程之间是共享进程内数据的。

    假设各个线程仅仅是从共享数据中读取数据,则比較easy实现数据共享。比如,我提供一个高考成绩查询的接口,每一次查询请求都会启动一个线程,多个线程共享高考成绩的数据;

    然而,实际情况中,非常多时候都须要多个线程共同维护一份共享数据。

    比如,缓存框架,它是以维护一份公共缓存为基础,当多个对象取缓存时,须要进行同步处理。或者说多个线程。须要控制器运行顺序,这是也须要使用锁或者同步机制。

    也就是说。线程的运行是竞相的,我们须要使用一定的机制,控制其并发。


    拿以下一个卖票的样例来举例(此实例来自海涛博客):

    如今一共三个窗体,共同拥有20张票。首先,我们先不正确三个窗体进行控制。

    三个窗体同一时候卖票:

    package com.lzq.thread;
    
    public class Test3 implements Runnable {
    	private Integer ticket = 20;
    	public void run() {
    		System.out.println(Thread.currentThread().getName() + ": 開始卖票!");
    		while (ticket > 0) {
    			try {
    				Thread.currentThread().sleep(1000);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName() + ":卖掉 " + ticket--
    					+ " 号票 ");
    		}
    	}
    	public static void main(String[] args) {
    		Test3 test = new Test3();
    		Thread t1 = new Thread(test, "1号窗体");
    		Thread t2 = new Thread(test, "2号窗体");
    		Thread t3 = new Thread(test, "3号窗体");
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    }
    


    执行结果最后会出现:


    我们明明限定了while(ticked>0)的情况,但是当卖完最后一张票后,还是卖了第“0”张票和第“-1”张票,这是因为当仅仅剩下最后一张票时,三个窗体(即三个进程)都满足ticked>0的条件,就进入到了while循环里面,然后就卖了三张票:第“1”张票、第“0”张票和第“-1”张票。

    这样的情况的优点就是:同一时刻。三个窗体可以同一时候售票。可以实现高速售票。然而,这样的方式存在数据同步问题。


    以下,我们採用Java中的同步机制,解决上面的同步问题。

    public class Test3 implements Runnable {
    	private static int ticketCount = 10;
    	public void run() {
    		Thread currentThread = Thread.currentThread();
    		System.out.println(currentThread.getName() + ": 開始卖票。");
    		while (true) {
    			synchronized ("2") {//採用同步机制
    				if (ticketCount > 0) {
    					try {
    						currentThread.sleep(1000);
    					} catch (InterruptedException e) {
    						e.printStackTrace();
    					}
    					System.out.println(Thread.currentThread().getName() + ":卖掉 "
    							+ ticketCount-- + " 号票 ");
    				}
    			}
    		}
    	}
    }


    结果如图所看到的:



    而这时候採用同步机制。每一个线程近来的时候。都会加上把锁。然后再推断ticketCount是否大于零。

    一旦有线程拿到了锁,则其它线程则仅仅能在外等待,无法进入。等拿到锁的对象完毕操作,释放锁对象之后,其它线程才干进入。

    依次类推。

    所以这样的处理方式可以保证程序的正确性,不出现售出第“0”张票、第“-1”张票的情况。可是同一时刻。仅仅能有一个线程拿到锁。运行速度比較缓慢。



    使用synchronizedkeyword,仅仅是处理多线程的一种方式,Java的JDK中还提供了重入锁、读写锁、Condition对象、Semaphore信号量以及ThreadLocal线程局部变量等等并发控制方法。兴许文章中会陆续介绍。

    以我对多线程的了解,大家还是要多看一些底层的东西,不使用那些工具,直接使用Runnable接口和Thread来进行线程控制。使用多了,JDK中提供的各种多线程的工具一看也就会用了。在多线程编程中,最重要的并非如何使用多线程编程,而是要明确什么时候须要使用多线程的问题。

    而这一步。却须要我们不断的积累,才可以做到。光凭借看书、学习是远远不够的。


    本篇文章,阐述了线程与进程之间的关系,介绍了多线程的创建方式,以及我推荐大家的方式及理由。最后由介绍了多线程的使用中遇到的问题,怎样更好的使用多线程。提供了思路。谢谢关注。

  • 相关阅读:
    剑指offer-删除链表中重复的节点
    剑指offer——二叉树中和为某一值的路径
    动态规划之140 Word Break2
    动态规划之139 Word Break
    动态规划之132 Palindrome Partitioning II
    动态规划之115 Distinct Subsequences
    动态规划之97 Interleaving String
    动态规划之91 decode ways
    vector
    第一篇
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/7357350.html
Copyright © 2011-2022 走看看