`
zh_harry
  • 浏览: 102528 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
877aca81-daac-33c8-8bf9-3a886cebc6c3
自己动手写java 框架
浏览量:28435
社区版块
存档分类
最新评论

疯子在思考之java 线程的那点事儿

    博客分类:
  • JAVA
阅读更多
很长时间没写博客了,最近事情比较多
之前在文章中提到过tomcat 的main函数在哪?被很多朋友拍砖了
今天继续就这话题展开,先了解几个线程有关的概念
1、多线程 multithread
为什么要用多线程?就是让cpu别太闲,有空就要干活,提高效率。
2、线程池 threadpool
为什么要用线程池,所有跟池相关的,如connectionPool(数据库连接池),ajax request请求对 象池、线程池等都是为了减少对象new所带来的开销.
3、线程安全 thread safe
所谓的线程安全就是指多线程的运行结果与单线程的运行结果一致,java 通过synchronized和threadlocal等解决线程安全问题。
线程不安全是由于对共享资源(如static 变量 单例成员变量 文件 数据库 缓存等)的同步写操作而引起的。

tomcat 的main在这里
它会启动一个serviceSoket监听客户端socket请求.
org.apache.catalina.startup.Bootstrap.java
具体功能有一本书《深入剖析Tomcat》有兴趣的朋友可以了解一下。
我们先用最简单的socket 看看他的工作原理
服务器 socket代码
package service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ServiceSocketDemo {

	/**
	 * @author zlz
	 * 
	 * @time 2013-7-26下午5:00:04
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
                //服务器端启动后会不停地监听该端口请求
		ServerSocket serverSocket = new ServerSocket(1999);
		while (true) {
                        //接受请求
			Socket socket = serverSocket.accept();
			BufferedReader br = new BufferedReader(new InputStreamReader(
					socket.getInputStream()));
			String msg = null;
			if ((msg = br.readLine()) != null) {
				System.out.println("服务器端收到—->" + msg);
			}
			PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
                         
//处理后并输出到客户端
			pw.println("hi");
			socket.close();
		}
	}
}



客户端socket代码
package client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class ClientSocketDemo {

	/**
	 * @author zlz
	 * 
	 * @time 2013-7-26下午5:03:52
	 * @param args
	 * @throws IOException
	 * @throws UnknownHostException
	 */
	public static void main(String[] args) throws UnknownHostException,
			IOException {
                //请求127.0.0.1:1999端口
		Socket socket = new Socket("127.0.0.1", 1999);
		PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
                //发送hello 并等待服务器端处理
		pw.println("helllo");                
		BufferedReader br = new BufferedReader(new InputStreamReader(
				socket.getInputStream()));
		String msg = null;
		if ((msg = br.readLine()) != null) {
			System.out.println("客户端收到—->" + msg);
		}
	}

}


沿着多线程的思路给这个最简单的socket加上多线程
方案1:将请求的数据扔给线程(这个方案是行不通的,因为数据处理完毕之后无法返回给客户端,需要返回到主线程处理,这就相当于是脱了裤子放屁,多此一举)
方案2:将socket对象扔给线程(这一点其实很重要,主线程不要再关心子线程的处理结果,下面要继续说到主线程需要关心子线程结果的情况 mapreduce)

方案2是多线程的一种实现方案,但是还不够优化,每次请求都要new一个线程,增大了服务器的压力,所以tomcat引入了threadpool的概念。
实现threadpool与其他的pool不太一样,因为线程程行完成之后就不能再restart了,即一旦执行完成将不能再复用。那么怎么实现线程池呢?

解决办法:在线程内while无线循环,永远不死。

继续拿tomcat的例子
tomcat会有一个sockt队列(当然对它的访问要线程安全的)
主线程负责生产
子线程负责处理
即生产者/消费者模式
显然这里比上边的多线程多了一个队列,这是我所理解的线程池概念,有异意大家批评。

刚才提到这里的主线程不需要子线程返回结果
而有些场景我们是需要子线程反馈的,比如mapreduce,它是将一份数据(一般比较大)
分成N份给N个线程这是map的过程,然后每个线程的处理结果在汇总到主线程这是reduce的过程。那么就需要线程间通信。
线程通过需要全局变量,即主线程和子线程共享能够访问的变量。
举一个简单例子
比如一本汉语字典,要查一下字典中一共有多少个字,那么我们分成N个线程去同步处理。
需要一个变量去统计已经处理的线程总数(dealedThreadCount),还有一个字典对象。
这个变量一定是共享的(或者是static 或者所在类是单例的)
那么主线程的处理逻辑应该是这样的
伪代码:
static Integer dealedThreadCount
synchronized(dealedThreadCount){
    for(int i=0;i<n;i++{
      //子线程处理
      new DealThread(i).start();
    }
    //主线程等待
    dealedThreadCount.wait();
}

子线程代码
...
dealedThreadCount++;
if(dealedThreadCount==n)
{
dealedThreadCount.nodify();通知主线程reduce
}

总结:
synchronized的变量只要是全局共享的即可,可以没有任何意义,为了实现线程间通信
需要synchronized wait notify 辅助完成
一般出现wait notify的情况一定会出现synchronized,反之则不一定。



最后一点关于线程安全的threadlocal
见这里
http://lizhizhang.iteye.com/admin/blogs/1909765

这里继续说一下,上边的链接讲了threadlocal原理及java sdk 的代码分析。
那么什么场景应用呢?
我所了解的有两个地方
第一 数据库访问时的session对象
第二 不知道朋友们思考过没有,strtus 2.0有一个actionsupport对象,这里会注入request response等对象
是因为他们是多实例对象所以不会有问题,那么如果我想把它定义成单例的(spring mvc是这样做的)就会有线程安全问题。而且我们同样要继承该类,那么就需要threadlocal变量。


线程安全问题非常值得注意
比如jdk里的很多对象都是线程不安全的,而有些变量看起来是不安全的,而实际上又是安全的。举两个例子

//线程安全的 getInstance一般为单例的方法,但jdk里每次都会new 一个变量出现。
Calendar c=Calendar.getInstance();
 /**
     * Gets a calendar using the default time zone and locale. The
     * <code>Calendar</code> returned is based on the current time
     * in the default time zone with the default locale.
     *
     * @return a Calendar.
     */
    public static Calendar getInstance()
    {
        Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault());
	cal.sharedZone = true;
	return cal;
    }

 private static Calendar createCalendar(TimeZone zone,
					   Locale aLocale)
    {
	// If the specified locale is a Thai locale, returns a BuddhistCalendar
	// instance.
	if ("th".equals(aLocale.getLanguage())
	    && ("TH".equals(aLocale.getCountry()))) {
	    return new sun.util.BuddhistCalendar(zone, aLocale);
	} else if ("JP".equals(aLocale.getVariant())
		   && "JP".equals(aLocale.getCountry())
		   && "ja".equals(aLocale.getLanguage())) {
	    return new JapaneseImperialCalendar(zone, aLocale);
	}	    

	// else create the default calendar
        return new GregorianCalendar(zone, aLocale);	
    }


第二个
DateFormat s=SimpleDateFormat.getInstance();
都是非单例对象,即是线程安全的
但是每次new的开销是比较大的,当数据量访问量非常大时会导致cpu过高,解决办法也是通过threadlocal


jdk中其他线程问题,各位大拿可以补充,谢谢!
3
4
分享到:
评论
7 楼 zh_harry 2013-08-16  
wxl24life 写道
楼主好文,提几个明显的概念上的误解

1、
引用
为什么要用线程池,所有跟池相关的,如connectionPool(数据库连接池),ajax request请求对 象池、线程池等都是为了减少对象new所带来的开销.


java 1.5 提供的线程池的作用可远不只是为了减少 new thread 的开销问题哦

2、
引用
线程不安全是由于对共享资源(如static 变量 单例成员变量 文件 数据库 缓存等)的同步写操作而引起的。


这里是另一个很严重的误解,线程安全问题是由于对共享可变资源的读写并发访问产生的

1.5的线程池我还不了解,还停留在1.4版本上
共享可变是对的(我文中也说的是变量,并不是常量),但主要是写操作,读操作不产生线程不安全问题
6 楼 youjianbo_han_87 2013-08-15  
这种网络IO,建议用NIO实现,异步非阻塞。
5 楼 wxl24life 2013-08-15  
楼主好文,提几个明显的概念上的误解

1、
引用
为什么要用线程池,所有跟池相关的,如connectionPool(数据库连接池),ajax request请求对 象池、线程池等都是为了减少对象new所带来的开销.


java 1.5 提供的线程池的作用可远不只是为了减少 new thread 的开销问题哦

2、
引用
线程不安全是由于对共享资源(如static 变量 单例成员变量 文件 数据库 缓存等)的同步写操作而引起的。


这里是另一个很严重的误解,线程安全问题是由于对共享可变资源的读写并发访问产生的
4 楼 zh_harry 2013-08-15  
QuarterLifeForJava 写道
就是说,我们只要用别人的就行了,会用就可以了。怎么开发出来的,开发的痛苦是老外承受着的

是这个意思
我现在在想链接池和log4j的实现
datasource只有getConnection接口有用,log4j和jtli怎么兼容?tomcat做到了
dbcp和c3p0怎么搞的?
有兴趣的一起讨论
3 楼 QuarterLifeForJava 2013-08-15  
就是说,我们只要用别人的就行了,会用就可以了。怎么开发出来的,开发的痛苦是老外承受着的
2 楼 zh_harry 2013-08-15  
lvwenwen 写道
看到国外开发人员的痛苦,我内心很淡定的安慰了自己。

神马意思?
1 楼 lvwenwen 2013-08-14  
看到国外开发人员的痛苦,我内心很淡定的安慰了自己。

相关推荐

    疯子卸载java.zip

    本文将详细介绍如何在不同操作系统上正确卸载Java。 一、Windows系统下的Java卸载 1. **控制面板法**:打开“控制面板”,选择“程序”或“程序和功能”选项,这里会列出已安装的所有程序。在列表中找到所有与Java...

    《天才在左疯子在右》的基本介绍和推荐理由[定义].pdf

    《天才在左疯子在右》的基本介绍和推荐理由[定义].pdf

    2017年阿里Java基础面试题文档 Java知识分享

    这些知识点不仅涵盖了Java语言的基础,还包括了面向对象编程、多线程、数据库等多个方面的内容。面试官通过这些问题,可以全面评估候选人的技术水平和实践经验。对于求职者而言,熟悉并掌握这些知识点对于通过面试至...

    java少量使用函数

    在"java少量使用函数"这个主题中,我们将探讨Java中的核心概念,包括`java.io`包,线程通信,正则表达式,数据结构,排序算法以及动态设计模式。以下是对这些知识点的详细讲解: 1. **`java.io`包**:这是Java中的...

    疯子手机apk卸载工具

    疯子手机apk卸载工具疯子手机apk卸载工具疯子手机apk卸载工具疯子手机apk卸载工具疯子手机apk卸载工具疯子手机apk卸载工具疯子手机apk卸载工具疯子手机apk卸载工具疯子手机apk卸载工具疯子手机apk卸载工具疯子手机...

    疯子CMS_轻简杰奇小说系统.zip

    【疯子CMS-轻简小说系统】是一款专为小说网站设计的内容管理系统(CMS),它以简洁易用为核心理念,旨在让网站管理员能够快速搭建和管理自己的小说平台,无需复杂的操作和大量的技术背景。这款系统可能包含以下关键...

    疯子锁IE插件源码程序演示效果

    【标签】"疯子锁ie插件,锁ie,"再次确认了主要关注点,即该插件是针对IE浏览器的锁定解决方案。 【压缩包子文件的文件名称列表】中的"疯子锁IE插件演示效果.exe"表明,这是一个可执行文件,用于展示疯子锁IE插件的...

    Android疯子卸载Android疯子卸载

    在Android开发中,Java是主要的编程语言之一。开发者通过编写Java代码来实现Android应用的功能,包括用户界面、数据处理、网络通信等。Java的特点包括平台无关性、垃圾回收机制和丰富的类库,这些使得它在Android...

    java_8_lambdas

    Java 8在编程语言的发展历程中是一个重要的里程碑,引入了多项改变和增强功能,其中最为引人注目且广泛应用的特性之一就是Lambda表达式。Lambda表达式为Java带来了函数式编程的特性,极大地方便了开发人员编写更加...

    DELPHI 的仿QQ疯子 不完全

    接下来,我们将详细讨论DELPHI编程语言以及在实现仿QQ疯子过程中可能涉及的关键技术点。 DELPHI是Borland公司推出的一种面向对象的、快速应用程序开发(RAD)工具,基于Pascal语言。它以其高效的编译器和强大的VCL...

    手机助手 疯子助手

    疯子助手是一款为苹果设备用户提供应用下载和游戏破解的软件,疯子助手里提供了海量不闪退应用和近千款极高有效度的ios热门游戏存档。 疯子助手完美支持不越狱的iPhone和iPad等iOS设备,是一款下载安装和管理软件、...

    疯子苹果助手 v1.0.1.318.zip

    首先,疯子苹果助手的特性之一是其海量的正版软件和游戏资源。用户可以在该平台上找到众多官方认证的应用,涵盖娱乐、教育、工具、社交等多个领域,满足不同用户的个性化需求。每日更新的新增应用确保了用户始终能够...

    疯子ftp上传工具源码传送数据到服务器源码版.rar

    在这个"疯子ftp上传工具源码传送数据到服务器源码版.rar"压缩包中,包含的源码可能是一个实现了FTP协议的上传工具,允许用户将本地文件上传至远程服务器。 FTP上传工具的核心功能通常包括以下部分: 1. **连接建立...

    java开发的贪吃蛇

    在本项目中,"java开发的贪吃蛇"是一个基于Java编程语言实现的经典游戏——贪吃蛇。这个项目使用了JDK1.7版本,意味着它兼容Java 7的特性,例如try-with-resources语句、多租户API等。开发者使用MyEclipse作为集成...

    java程序员面试大全

    在面试准备阶段,了解和掌握这些知识点显得尤为重要。下面将详细解读文档中提及的各个知识点。 JAVA的基本数据类型包括int, long, short, byte, float, double, char以及boolean。在Java中,String不是基本数据类型...

    读后感《天才在左,疯子在右》.doc

    《天才在左,疯子在右》这本书以其独特的视角探讨了人类思维的边界,通过对话精神病人的经历,引发读者对自我认知和世界本质的深度思考。以下是对书中的几个核心观点的解读: 首先,作者通过描述吃苹果的过程,强调...

    初中语文文摘社会疯子

    【描述】: 该文摘通过讲述一个看似疯子的人物,揭示了社会中某些不正常的现象,引发对正常与疯狂界限的思考。 【标签】: 资料 【正文】: 这篇文摘通过一个特殊的角色——"疯子",探讨了社会规范和个人行为之间的...

    电脑疯子GHOST-WIN7-SP1旗舰版(64位).doc编程资料

    电脑疯子GHOST-WIN7-SP1旗舰版(64位).doc编程资料

    读后感ۥ天才在左疯子在右精选.doc

    《天才在左,疯子在右》这本书以其独特的视角揭示了人类思维的多样性和深度,让人重新审视何为正常,何为异常。书中的故事和人物让我们意识到,那些被我们视为“疯狂”的想法,也许正是创新和突破的源泉。以下是对书...

    族谱(java-ssm)

    在本项目中,Spring负责管理对象的生命周期和依赖注入,同时提供了AOP(面向切面编程)功能,用于处理如日志、事务等横切关注点。 2. **SpringMVC**:SpringMVC是Spring框架的一个模块,专门用于构建Web应用。它...

Global site tag (gtag.js) - Google Analytics