Wakeups: 这在汉字信息编码码里是什么意思

linzhaojie525的博客
https://blog.csdn.net/
https://static-blog.csdn.net/images/logo.gif
https://blog.csdn.net/linzhaojie525
https://blog.csdn.net/
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
注册码:Product Code:4t46t6vydkvsxekkvf3fjnpzy5wbuhphqzserial Number:601769
password:xs374ca
本人版本 Version 11.0.2.1766
亲测,可用
作者:linzhaojie525 发表于
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
这里我们介绍的是 40+ 个非常有用的 Oracle 查询语句,主要涵盖了日期操作,获取服务器信息,获取执行状态,计算数据库大小等等方面的查询。这些是所有 Oracle 开发者都必备的技能,所以快快收藏吧!
日期/时间 相关查询
获取当前月份的第一天
运行这个命令能快速返回当前月份的第一天。你可以用任何的日期值替换 “SYSDATE”来指定查询的日期。
SELECT TRUNC (SYSDATE, 'MONTH') "First day of current month"
FROM DUAL;
获取当前月份的最后一天
这个查询语句类似于上面那个语句,而且充分照顾到了闰年,所以当二月份有 29 号,那么就会返回 29/2 。你可以用任何的日期值替换 “SYSDATE”来指定查询的日期。
SELECT TRUNC (LAST_DAY (SYSDATE)) "Last day of current month"
FROM DUAL;
获取当前年份的第一天
每年的第一天都是1 月1日,这个查询语句可以使用在存储过程中,需要对当前年份第一天做一些计算的时候。你可以用任何的日期值替换 “SYSDATE”来指定查询的日期。
SELECT TRUNC (SYSDATE, 'YEAR') "Year First Day" FROM DUAL;
获取当前年份的最后一天
类似于上面的查询语句。你可以用任何的日期值替换 “SYSDATE”来指定查询的日期。
SELECT ADD_MONTHS (TRUNC (SYSDATE, 'YEAR'), 12) - 1 "Year Last Day" FROM DUAL
获取当前月份的天数
这个语句非常有用,可以计算出当前月份的天数。你可以用任何的日期值替换 “SYSDATE”来指定查询的日期。
SELECT CAST (TO_CHAR (LAST_DAY (SYSDATE), 'dd') AS INT) number_of_days
FROM DUAL;
获取当前月份剩下的天数
下面的语句用来计算当前月份剩下的天数。你可以用任何的日期值替换 “SYSDATE”来指定查询的日期。
SELECT SYSDATE,
LAST_DAY (SYSDATE) "Last",
LAST_DAY (SYSDATE) - SYSDATE "Days left"
FROM DUAL;
获取两个日期之间的天数
使用这个语句来获取两个不同日期自检的天数。
SELECT ROUND ( (MONTHS_BETWEEN ('01-Feb-2014', '01-Mar-2012') * 30), 0)
num_of_days
FROM DUAL;
SELECT TRUNC(sysdate) - TRUNC(e.hire_date) FROM
如果你需要查询一些特定日期的天数,可以使用第二个查询语句。这个例子是计算员工入职的天数。
显示当前年份截止到上个月每个月份开始和结束的日期
这个是个很聪明的查询语句,用来显示当前年份每个月的开始和结束的日期,你可以使用这个进行一些类型的计算。你可以用任何的日期值替换 “SYSDATE”来指定查询的日期。
SELECT ADD_MONTHS (TRUNC (SYSDATE, 'MONTH'), i) start_date,
TRUNC (LAST_DAY (ADD_MONTHS (SYSDATE, i))) end_date
FROM XMLTABLE (
'for $i in 0 to xs:int(D) return $i'
PASSING XMLELEMENT (
MONTHS_BETWEEN (
ADD_MONTHS (TRUNC (SYSDATE, 'YEAR') - 1, 12),
SYSDATE)))
COLUMNS i INTEGER PATH '.');
获取直到目前为止今天过去的秒数(从 00:00 开始算)
SELECT (SYSDATE - TRUNC (SYSDATE)) * 24 * 60 * 60 num_of_sec_since_morning
FROM DUAL;
获取今天剩下的秒数(直到 23:59:59 结束)
SELECT (TRUNC (SYSDATE+1) - SYSDATE) * 24 * 60 * 60 num_of_sec_left
FROM DUAL;
数据字典查询
检查在当前数据库模式下是否存在指定的表
这是一个简单的查询语句,用来检查当前数据库是否有你想要创建的表,允许你重新运行创建表脚本,这个也可以检查当前用户是否已经创建了指定的表(根据这个查询语句在什么环境下运行来查询)。
SELECT table_name
FROM user_tables
WHERE table_name = 'TABLE_NAME';
检查在当前表中是否存在指定的列
这是个简单的查询语句来检查表里是否有指定的列,在你尝试使用 ALTER TABLE 来添加新的列新到表中的时候非常有用,它会提示你是否已经存在这个列。
SELECT column_name AS FOUND
FROM user_tab_cols
WHERE table_name = 'TABLE_NAME' AND column_name = 'COLUMN_NAME';
显示表结构
这 个查询语句会显示任何表的 DDL 状态信息。请注意我们已经将‘TABLE’作为第一个信息提交了。这个查询语句也可以用来获取任何数据库对象的 DDL 状态信息。举例说明,只需要把第一个参数替换成‘VIEW’,第二个修改成视图的名字,就可以查询视图的 DDL 信息了。
SELECT DBMS_METADATA.get_ddl ('TABLE', 'TABLE_NAME', 'USER_NAME') FROM DUAL;
获取当前模式
这是另一个可以获得当前模式的名字的查询语句。
SELECT SYS_CONTEXT ('userenv', 'current_schema') FROM DUAL;
修改当前模式
这是另一个可以修改当前模式的查询语句,当你希望你的脚本可以在指定的用户下运行的时候非常有用,而且这是非常安全的一个方式。
ALTER SESSION SET CURRENT_SCHEMA = new_
数据库管理查询
数据库版本信息
返回 Oracle 数据库版本
SELECT * FROM v$version;
数据库默认信息
返回一些系统默认的信息
SELECT username,
default_tablespace,
temporary_tablespace
数据库字符设置信息
显示数据库的字符设置信息
SELECT * FROM nls_database_
获取 Oracle 版本
SELECT VALUE
FROM v$system_parameter
WHERE name = 'compatible';
存储区分大小写的数据,但是索引不区分大小写
某些时候你可能想在数据库中查询一些独立的数据,可能会用 UPPER(..) = UPPER(..) 来进行不区分大小写的查询,所以就想让索引不区分大小写,不占用那么多的空间,这个语句恰好能解决你的需求 。
CREATE TABLE tab (col1 VARCHAR2 (10));
CREATE INDEX idx1
ON tab (UPPER (col1));
ANALYZE TABLE a COMPUTE STATISTICS;
调整没有添加数据文件的表空间
另一个 DDL 查询来调整表空间大小
ALTER DATABASE DATAFILE '/work/oradata/STARTST/STAR02D.dbf' resize 2000M;
检查表空间的自动扩展开关
在给定的表空间中查询是否打开了自动扩展开关
SELECT SUBSTR (file_name, 1, 50), AUTOEXTENSIBLE FROM dba_data_
SELECT tablespace_name, AUTOEXTENSIBLE FROM dba_data_
在表空间添加数据文件
在表空间中添加数据文件
ALTER TABLESPACE data01 ADD DATAFILE '/work/oradata/STARTST/data01.dbf'
SIZE 1000M AUTOEXTEND OFF;
增加数据文件的大小
给指定的表空间增加大小
ALTER DATABASE DATAFILE '/u01/app/Test_data_01.dbf' RESIZE 2G;
查询数据库的实际大小
给出以 GB 为单位的数据库的实际大小
SELECT SUM (bytes) / 1024 / 1024 / 1024 AS GB FROM dba_data_
查询数据库中数据占用的大小或者是数据库使用细节
给出在数据库中数据占据的空间大小
SELECT SUM (bytes) / 1024 / 1024 / 1024 AS GB FROM dba_
查询模式或者用户的大小
以 MB 为单位给出用户的空间大小
SELECT SUM (bytes / 1024 / 1024) "size"
FROM dba_segments
WHERE owner = '&owner';
查询数据库中每个用户最后使用的 SQL 查询
此查询语句会显示当前数据库中每个用户最后使用的 SQL 语句。
SELECT S.USERNAME || '(' || s.sid || ')-' || s.osuser UNAME,
s.program || '-' || s.terminal || '(' || s.machine || ')' PROG,
s.sid || '/' || s.serial# sid,
s.status "Status",
sql_text sqltext
FROM v$sqltext_with_newlines t, V$SESSION s, v$process p
t.address = s.sql_address
AND p.addr = s.paddr(+)
AND t.hash_value = s.sql_hash_value
ORDER BY s.sid, t.
性能相关查询
查询用户 CPU 的使用率
这个语句是用来显示每个用户的 CPU 使用率,有助于用户理解数据库负载情况
SELECT ss.username, se.SID, VALUE / 100 cpu_usage_seconds
FROM v$session ss, v$sesstat se, v$statname sn
se.STATISTIC# = sn.STATISTIC#
AND NAME LIKE '%CPU used by this session%'
AND se.SID = ss.SID
AND ss.status = 'ACTIVE'
AND ss.username IS NOT NULL
ORDER BY VALUE DESC;
查询数据库长查询进展情况
显示运行中的长查询的进展情况
SELECT a.sid,
a.serial#,
b.username,
opname OPERATION,
target OBJECT,
TRUNC (elapsed_seconds, 5) "ET (s)",
TO_CHAR (start_time, 'HH24:MI:SS') start_time,
ROUND ( (sofar / totalwork) * 100, 2) "COMPLETE (%)"
FROM v$session_longops a, v$session b
a.sid = b.sid
AND b.username NOT IN ('SYS', 'SYSTEM')
AND totalwork & 0
ORDER BY elapsed_
获取当前会话 ID,进程 ID,客户端 ID 等
这个专门提供给想使用进程 ID 和 会话 ID 做些 voodoo magic 的用户。
SELECT b.sid,
b.serial#,
a.spid processid,
b.process clientpid
FROM v$process a, v$session b
WHERE a.addr = b.paddr AND b.audsid = USERENV ('sessionid');
V$SESSION.SID AND V$SESSION.SERIAL# 是数据库进程 ID
V$PROCESS.SPID 是数据库服务器后台进程 ID
V$SESSION.PROCESS 是客户端 PROCESS ID, ON windows it IS : separated THE FIRST # IS THE PROCESS ID ON THE client AND 2nd one IS THE THREAD id.
查询特定的模式或者表中执行的最后一个 SQL 语句
SELECT CREATED, TIMESTAMP, last_ddl_time
FROM all_objects
OWNER = 'MYSCHEMA'
AND OBJECT_TYPE = 'TABLE'
AND OBJECT_NAME = 'EMPLOYEE_TABLE';
查询每个执行读取的前十个 SQL
SELECT ROWNUM,
SUBSTR (a.sql_text, 1, 200) sql_text,
a.disk_reads / DECODE (a.executions, 0, 1, a.executions))
reads_per_execution,
a.buffer_gets,
a.disk_reads,
a.executions,
FROM v$sqlarea a
ORDER BY 3 DESC)
WHERE ROWNUM & 10;
在视图中查询并显示实际的 Oracle 连接
SELECT osuser,
FROM v$session
查询并显示通过打开连接程序打开连接的组
SELECT program application, COUNT (program) Numero_Sesiones
FROM v$session
GROUP BY program
ORDER BY Numero_Sesiones DESC;
查询并显示连接 Oracle 的用户和用户的会话数量
SELECT username Usuario_Oracle, COUNT (username) Numero_Sesiones
FROM v$session
GROUP BY username
ORDER BY Numero_Sesiones DESC;
获取拥有者的对象数量
SELECT owner, COUNT (owner) number_of_objects
FROM dba_objects
GROUP BY owner
ORDER BY number_of_objects DESC;
实用/数学 相关的查询
把数值转换成文字
更多信息可以查看:
SELECT TO_CHAR (TO_DATE (1526, 'j'), 'jsp') FROM DUAL;
one thousand five hundred twenty-six
在包的源代码中查询字符串
这个查询语句会在所有包的源代码上搜索‘FOO_SOMETHING’ ,可以帮助用户在源代码中查找特定的存储过程或者是函数调用。
FROM dba_source
WHERE UPPER (text) LIKE '%FOO_SOMETHING%'
AND owner = 'USER_NAME';
把用逗号分隔的数据插入的表中
当 你想把用逗号分隔开的字符串插入表中的时候,你可以使用其他的查询语句,比如 IN 或者是 NOT IN 。这里我们把‘AA,BB,CC,DD,EE,FF’转换成包含 AA,BB,CC 等作为一行的表,这样你就很容易把这些字符串插入到其他表中,并快速的做一些相关的操作。
AS (SELECT 'AA,BB,CC,DD,EE,FF'
AS csvdata
FROM DUAL)
SELECT REGEXP_SUBSTR (csv.csvdata, '[^,]+', 1, LEVEL) pivot_char
FROM DUAL, csv
CONNECT BY REGEXP_SUBSTR (csv.csvdata,'[^,]+', 1, LEVEL) IS NOT NULL;
查询表中的最后一个记录
这个查询语句很直接,表中没有主键,或者是用户不确定记录最大主键是否是最新的那个记录时,就可以使用这个语句来查询表中最后一个记录。
FROM employees
WHERE ROWID IN (SELECT MAX (ROWID) FROM employees);
SELECT * FROM employees
FROM employees
WHERE ROWNUM & (SELECT COUNT (*) FROM employees);
在 Oracle 中做行数据乘法
这个查询语句使用一些复杂的数学函数来做每个行的数值乘法。更多内容请查阅:
AS (SELECT -2 num FROM DUAL
SELECT -3 num FROM DUAL
SELECT -4 num FROM DUAL),
AS (SELECT CASE MOD (COUNT (*), 2) WHEN 0 THEN 1 ELSE -1 END val
WHERE num & 0)
SELECT EXP (SUM (LN (ABS (num)))) * val
FROM tbl, sign_val
在 Oracle 生成随机数据
每个开发者都想能轻松生成一堆随机数据来测试数据库多好,下面这条查询语句就可以满足你,它可以在 Oracle 中生成随机的数据插入到表中。详细信息可以查看
SELECT LEVEL empl_id,
MOD (ROWNUM, 50000) dept_id,
TRUNC (DBMS_RANDOM.VALUE (1000, 500000), 2) salary,
DECODE (ROUND (DBMS_RANDOM.VALUE (1, 2)),
2, 'F') gender,
ROUND (DBMS_RANDOM.VALUE (1, 28))
|| ROUND (DBMS_RANDOM.VALUE (1, 12))
|| ROUND (DBMS_RANDOM.VALUE (1900, 2010)),
'DD-MM-YYYY')
DBMS_RANDOM.STRING ('x', DBMS_RANDOM.VALUE (20, 50)) address
CONNECT BY LEVEL & 10000;
在 Oracle 中生成随机数值
这是 Oracle 普通的旧的随机数值生成器。这个可以生成 0-100 之间的随机数值,如果你想自己设置数值范围,那么改变乘数就可以了。
SELECT ROUND (DBMS_RANDOM.VALUE () * 100) + 1 AS random_num FROM DUAL;
检查表中是否含有任何的数据
这个可以有很多中写法,你可以使用 count(*) 来查看表里的行的数量,但是这个查询语句比较高效和快速,而且我们只是想知道表里是否有任何的数据。
FROM TABLE_NAME
WHERE ROWNUM = 1;
如果你知道一些很好用的查询语句,可以减轻 Oracle 开发者的负担,那么在评论分享一下吧:)
作者:linzhaojie525 发表于
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
一、调用方式
我们知道,在servlet中调用转发、重定向的语句如下:
request.getRequestDispatcher("new.jsp").forward(request, response);
//转发到new.jsp
response.sendRedirect("new.jsp");
//重定向到new.jsp
在jsp页面中你也会看到通过下面的方式实现转发:
&jsp:forward page="apage.jsp" /&
当然也可以在jsp页面中实现重定向:
&%response.sendRedirect("new.jsp"); %& //重定向到new.jsp
二、本质区别
一句话,转发是服务器行为,重定向是客户端行为。为什么这样说呢,这就要看两个动作的工作流程:
转发过程:客户浏览器发送http请求——》web服务器接受此请求——》调用内部的一个方法在容器内部完成请求处理和转发动作——》将目标资源发送给客户;在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。在客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。
重定向过程:客户浏览器发送http请求——》web服务器接受后发送302状态码响应及对应新的location给客户浏览器——》客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址——》服务器根据此请求寻找资源并发送给客户。在这里location可以重定向到任意URL,既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向的路径,客户可以观察到地址的变化的。重定向行为是浏览器做了至少两次的访问请求的。
重定向,其实是两次request
第一次,客户端request
A,服务器响应,并response回来,告诉浏览器,你应该去B。这个时候IE可以看到地址变了,而且历史的回退按钮也亮了。重定向可以访问自己web应用以外的资源。在重定向的过程中,传输的信息会被丢失。
response.sendRedirect("loginsuccess.jsp");
请求转发是服务器内部把对一个request/response的处理权,移交给另外一个
对于客户端而言,它只知道自己最早请求的那个A,而不知道中间的B,甚至C、D。传输的信息不会丢失。
RequestDispatcher dis=request.getRequestDispatcher(“loginsuccess.jsp”);
Dis.forward(request,response);
假设你去办理某个执照
重定向:你先去了A局,A局的人说:“这个事情不归我们管,去B局”,然后,你就从A退了出来,自己乘车去了B局。
转发:你先去了A局,A局看了以后,知道这个事情其实应该B局来管,但是他没有把你退回来,而是让你坐一会儿,自己到后面办公室联系了B的人,让他们办好后,送了过来。
作者:linzhaojie525 发表于
https://blog.csdn.net/linzhaojie525/article/details/
阅读:185 评论:1
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
spring MVC之构造ModelAndView对象
----------
构造ModelAndView对象
当控制器处理完请求时,通常会将包含视图名称或视图对象以及一些模型属性的ModelAndView对象返回到DispatcherServlet。因此,经常需要在控制器中构造ModelAndView对象。ModelAndView类提供了几个重载的构造器和一些方便的方法,让你可以根据自己的喜好来构造ModelAndView对象。这些构造器和方法以类似的方式支持视图名称和视图对象。
当你只有一个模型属性要返回时,可以在构造器中指定该属性来构造ModelAndView对象:
package com.apress.springrecipes.court.
import org.springframework.web.servlet.ModelAndV
import org.springframework.web.servlet.mvc.AbstractC
public class WelcomeController extends AbstractController{
public ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response)throws Exception{
Date today = new Date();
return new ModelAndView("welcome","today",today);
如果有不止一个属性要返回,可以先将它们传递到一个Map中再来构造ModelAndView对象。
package com.apress.springrecipes.court.
import org.springframework.web.servlet.ModelAndV
import org. springframework.web.servlet.mvc.AbstractC
public class ReservationQueryController extends AbstractController{
public ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response)throws Exception{
Map&String,Object& model = new HashMap&String,Object&();
if(courtName != null){
model.put("courtName",courtName);
model.put("reservations",reservationService.query(courtName));
return new ModelAndView("reservationQuery",model);
Spring也提供了ModelMap,这是java.util.Map实现,可以根据模型属性的具体类型自动生成模型属性的名称。
package com.apress.springrecipes.court.
import org.springframework.ui.ModelM
import org.springframework.web.servlet.ModelAndV
import org.springframework.web.servlet.mvc.AbstractC
public class ReservationQueryController extends AbstractController{
public ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response)throws Exception{
ModelMap model = new ModelMap();
if(courtName != null){
model.addAttribute("courtName",courtName);
model.addAttribute("reservations",reservationService.query(courtName));
return new ModelAndView("reservationQuery",model);
由于这两个模型属性的类型为String和List&Reservation&,ModelMap会为它们生成默认的名称----string和reservationList。如果你不喜欢这些名称,可以显式地指定它们。
构造完ModelAndView对象之后,仍然可以利用addobject()方法为它添加模型属性。这个方法返回ModelAndView对象
本身,因此可以在一个语句中构造ModelAndView对象。请注意,你也可以省略addObject()方法的属性名称。在这种情况下,这个方法会与ModeMap生成相同的属性名称。
package com.apress.springrecipes.court.
import org.springframework.web.servlet.ModelAndV
import org.springframework.web.servlet.mvc.AbstractC
public class ReservationQueryController extends AbstractController{
public ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response)throws Exception{
List&Reservation& reservations = null;
if(courtName != null){
reservations = reservationService.query(courtName);
return new ModelAndView("reservationQuery","courtName",courtName)
.addObject("reservations",reservations);
事实上,返回的模型和视图都是可选的。在有些情况下,你只返回视图,模型中没有任何属性。或者只返回模型,让Spring MVC根据请求URL来决定视图。有时候,如果让控制器直接处理HttpServletResponse对象,甚至可以返回null,例如在将二进制文件返回给用户的时候。
作者:linzhaojie525 发表于
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
如果对什么是线程、什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内。
用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。
很多人都对其中的一些概念不够明确,如同步、并发等等,让我们先建立一个数据字典,以免产生误会。
多线程:指的是这个程序(一个进程)运行时产生了不止一个线程并行与并发:
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。
并发与并行
线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码:
void transferMoney(User from, User to, float amount){
to.setMoney(to.getBalance() + amount);
from.setMoney(from.getBalance() - amount);
同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。
好了,让我们开始吧。我准备分成几部分来总结涉及到多线程的内容:
扎好马步:线程的状态内功心法:每个对象都有的方法(机制)太祖长拳:基本线程类九阴真经:高级多线程控制类
扎好马步:线程的状态
先来两张图:
线程状态转换
各种状态一目了然,值得一提的是"Blocked"和"Waiting"这两个状态的区别:
线程在Running的过程中可能会遇到阻塞(Blocked)情况
对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。从jdk源码注释来看,blocked指的是对monitor的等待(可以参考下文的图)即该线程位于等待区。
线程在Running的过程中可能会遇到等待(Waiting)情况
线程可以主动调用object.wait或者sleep,或者join(join内部调用的是sleep,所以可看成sleep的一种)进入。从jdk源码注释来看,waiting是等待另一个线程完成某一个操作,如join等待另一个完成执行,object.wait()等待object.notify()方法执行。
Waiting状态和Blocked状态有点费解,我个人的理解是:Blocked其实也是一种wait,等待的是monitor,但是和Waiting状态不一样,举个例子,有三个线程进入了同步块,其中两个调用了object.wait(),进入了waiting状态,这时第三个调用了object.notifyAll(),这时候前两个线程就一个转移到了Runnable,一个转移到了Blocked。
从下文的monitor结构图来区别:每个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “ Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的线程状态Blocked,从jstack的dump中来看是 “Waiting for monitor entry”,而在 “Wait Set”中等待的线程状态是Waiting,表现在jstack的dump中是 “in Object.wait()”。
此外,在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable。
内功心法:每个对象都有的方法(机制)
synchronized, wait, notify 是任何对象都具有的同步工具。让我们先来了解他们
他们是应用于同步问题的人工线程调度工具。讲其本质,首先就要明确monitor的概念,Java中的每个对象都有一个监视器,来监测并发代码的重入。在非多线程编码时该监视器不发挥作用,反之如果在synchronized 范围内,监视器发挥作用。
wait/notify必须存在于synchronized块中。并且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着wait之后,其他线程可以进入同步块执行。
当某代码并不持有监视器的使用权时(如图中5的状态,即脱离同步块)去wait或notify,会抛出java.lang.IllegalMonitorStateException。也包括在synchronized块中去调用另一个对象的wait/notify,因为不同对象的监视器不同,同样会抛出此异常。
再讲用法:
synchronized单独使用:
代码块:如下,在多线程环境下,synchronized块中的方法获取了lock实例的monitor,如果实例相同,那么只有一个线程能执行该块内容
public class Thread1 implements Runnable {
public void run() {
synchronized(lock){
..do something
直接用于方法: 相当于上面代码中用lock来锁定的效果,实际获取的是Thread1类的monitor。更进一步,如果修饰的是static方法,则锁定该类所有实例。
public class Thread1 implements Runnable {
public synchronized void run() {
..do something
synchronized, wait, notify结合:典型场景生产者消费者问题
public synchronized void produce()
if(this.product &= MAX_PRODUCT)
System.out.println("产品已满,请稍候再生产");
catch(InterruptedException e)
e.printStackTrace();
this.product++;
System.out.println("生产者生产第" + this.product + "个产品.");
notifyAll();
public synchronized void consume()
if(this.product &= MIN_PRODUCT)
System.out.println("缺货,稍候再取");
catch (InterruptedException e)
e.printStackTrace();
System.out.println("消费者取走了第" + this.product + "个产品.");
this.product--;
notifyAll();
多线程的内存模型:main memory(主存)、working memory(线程栈),在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去(volatile关键词的作用:每次针对该变量的操作都激发一次load and save)。
针对多线程使用的变量如果不是volatile或者final修饰的,很有可能产生不可预知的结果(另一个线程修改了这个值,但是之后在某线程看到的是修改之前的值)。其实道理上讲同一实例的同一属性本身只有一个副本。但是多线程是会缓存值的,本质上,volatile就是不去缓存,直接取值。在线程安全的情况下加volatile会牺牲性能。
太祖长拳:基本线程类
基本线程类指的是Thread类,Runnable接口,Callable接口
Thread 类实现了Runnable接口,启动一个线程的方法:
  MyThread my = new MyThread();
  my.start();
Thread类相关方法:
public static Thread.yield()
public static Thread.sleep()
public join()
public interrupte()
关于中断:它并不像stop方法那样会中断一个正在运行的线程。线程会不时地检测中断标识位,以判断线程是否应该被中断(中断标识值是否为true)。终端只会影响到wait状态、sleep状态和join状态。被打断的线程会抛出InterruptedException。
Thread.interrupted()检查当前线程是否发生中断,返回boolean
synchronized在获锁的过程中是不能被中断的。
中断是一个状态!interrupt()方法只是将这个状态置为true而已。所以说正常运行的程序不去检测状态,就不会终止,而wait等阻塞方法会去检查并抛出异常。如果在正常运行的程序中添加while(!Thread.interrupted()) ,则同样可以在中断后离开代码体
Thread类最佳实践:
写的时候最好要设置线程名称 Thread.name,并设置线程组 ThreadGroup,目的是方便管理。在出现问题的时候,打印线程栈 (jstack -pid) 一眼就可以看出是哪个线程出的问题,这个线程是干什么的。
如何获取线程中的异常
不能用try,catch来获取线程中的异常
与Thread类似
future模式:并发模式的一种,可以有两种形式,即无阻塞和阻塞,分别是isDone和get。其中Future对象用来存放该线程的返回值以及状态
ExecutorService e = Executors.newFixedThreadPool(3);
Future future = e.submit(new myCallable());
future.isDone()
future.get()
九阴真经:高级多线程控制类
以上都属于内功心法,接下来是实际项目中常用到的工具了,Java1.5提供了一个非常高效实用的多线程包:java.util.concurrent, 提供了大量高级工具,可以帮助开发者编写高效、易维护、结构清晰的Java多线程程序。
1.ThreadLocal类
用处:保存线程的独立变量。对一个线程类(继承自Thread)
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。常用于用户登录控制,如记录session信息。
实现:每个Thread都持有一个TreadLocalMap类型的变量(该类是一个轻量级的Map,功能与map一样,区别是桶里放的是entry而不是entry的链表。功能还是一个map。)以本身为key,以目标为value。
主要方法是get()和set(T a),set之后在map里维护一个threadLocal -& a,get时将a返回。ThreadLocal是一个特殊的容器。
2.原子类(AtomicInteger、AtomicBoolean……)
如果使用atomic wrapper class如atomicInteger,或者使用自己保证原子的操作,则等同于synchronized
AtomicInteger.compareAndSet(int expect,int update)
该方法可用于实现乐观锁,考虑文中最初提到的如下场景:a给b付款10元,a扣了10元,b要加10元。此时c给b2元,但是b的加十元代码约为:
if(b.value.compareAndSet(old, value)){
AtomicReference
对于AtomicReference 来讲,也许对象会出现,属性丢失的情况,即oldObject == current,但是oldObject.getPropertyA != current.getPropertyA。
这时候,AtomicStampedReference就派上用场了。这也是一个很常用的思路,即加上版本号
3.Lock类 
lock: 在java.util.concurrent包内。共有三个实现:
ReentrantLockReentrantReadWriteLock.ReadLockReentrantReadWriteLock.WriteLock
主要目的是和synchronized一样, 两者都是为了解决同步问题,处理资源争端而产生的技术。功能类似但有一些区别。
区别如下:
lock更灵活,可以自由定义多把锁的枷锁解锁顺序(synchronized要按照先加的后解顺序)提供多种加锁方案,lock 阻塞式, trylock 无阻塞式, lockInterruptily 可打断式, 还有trylock的带超时时间版本。本质上和监视器锁(即synchronized是一样的)能力越大,责任越大,必须控制好加锁和解锁,否则会导致灾难。和Condition类的结合。性能更高,对比如下图:
synchronized和Lock性能对比
ReentrantLock    
可重入的意义在于持有锁的线程可以继续持有,并且要释放对等的次数后才真正释放该锁。
使用方法是:
1.先new一个实例
static ReentrantLock r=new ReentrantLock();
2.加锁      
.lock()或.lockInterruptibly();
此处也是个不同,后者可被打断。当a线程lock后,b线程阻塞,此时如果是lockInterruptibly,那么在调用b.interrupt()之后,b线程退出阻塞,并放弃对资源的争抢,进入catch块。(如果使用后者,必须throw interruptable exception 或catch)    
3.释放锁   
必须做!何为必须做呢,要放在finally里面。以防止异常跳出了正常流程,导致灾难。这里补充一个小知识点,finally是可以信任的:经过测试,哪怕是发生了OutofMemoryError,finally块中的语句执行也能够得到保证。
ReentrantReadWriteLock
可重入读写锁(读写锁的一个实现)
  ReentrantReadWriteLock lock = new ReentrantReadWriteLock()
  ReadLock r = lock.readLock();
  WriteLock w = lock.writeLock();
两者都有lock,unlock方法。写写,写读互斥;读读不互斥。可以实现并发读的高效线程安全代码
这里就讨论比较常用的两个:
BlockingQueueConcurrentHashMap
BlockingQueue
阻塞队列。该类是java.util.concurrent包下的重要类,通过对Queue的学习可以得知,这个queue是单向队列,可以在队列头添加元素和在队尾删除或取出元素。类似于一个管  道,特别适用于先进先出策略的一些应用场景。普通的queue接口主要实现有PriorityQueue(优先队列),有兴趣可以研究
BlockingQueue在队列的基础上添加了多线程协作的功能:
BlockingQueue
除了传统的queue功能(表格左边的两列)之外,还提供了阻塞接口put和take,带超时功能的阻塞接口offer和poll。put会在队列满的时候阻塞,直到有空间时被唤醒;take在队 列空的时候阻塞,直到有东西拿的时候才被唤醒。用于生产者-消费者模型尤其好用,堪称神器。
常见的阻塞队列有:
ArrayListBlockingQueueLinkedListBlockingQueueDelayQueueSynchronousQueue
ConcurrentHashMap
高效的线程安全哈希map。请对比hashTable , concurrentHashMap, HashMap
管理类的概念比较泛,用于管理线程,本身不是多线程的,但提供了一些机制来利用上述的工具做一些封装。
了解到的值得一提的管理类:ThreadPoolExecutor和 JMX框架下的系统级管理类 ThreadMXBeanThreadPoolExecutor
如果不了解这个类,应该了解前面提到的ExecutorService,开一个自己的线程池非常方便:
ExecutorService e = Executors.newCachedThreadPool();
ExecutorService e = Executors.newSingleThreadExecutor();
ExecutorService e = Executors.newFixedThreadPool(3);
e.execute(new MyRunnableImpl());
该类内部是通过ThreadPoolExecutor实现的,掌握该类有助于理解线程池的管理,本质上,他们都是ThreadPoolExecutor类的各种实现版本。请参见javadoc:
ThreadPoolExecutor参数解释
翻译一下:corePoolSize:池内线程初始值与最小值,就算是空闲状态,也会保持该数量线程。maximumPoolSize:线程最大值,线程的增长始终不会超过该值。keepAliveTime:当池内线程数高于corePoolSize时,经过多少时间多余的空闲线程才会被回收。回收前处于wait状态unit:
时间单位,可以使用TimeUnit的实例,如TimeUnit.MILLISECONDS workQueue:待入任务(Runnable)的等待场所,该参数主要影响调度策略,如公平与否,是否产生饿死(starving)threadFactory:线程工厂类,有默认实现,如果有自定义的需要则需要自己实现ThreadFactory接口并作为参数传入。
作者:linzhaojie525 发表于
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
java thread的运行周期中, 有几种状态, 在 java.lang.Thread.State 中有详细定义和说明:
NEW 状态是指线程刚创建, 尚未启动{新建(new)}
RUNNABLE 状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等 可运行(runnable):
这个状态下, 是在多个线程有同步操作的场景, 比如正在等待另一个线程的synchronized 块的执行释放, 或者可重入的 synchronized块里别人调用wait() 方法, 也就是这里是线程在等待进入临界区(阻塞状态);
这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll 一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在理解点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候,
也会进入WAITING状态, 等待被他join的线程执行结束
TIMED_WAITING
这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long), join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态
TERMINATED 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)
下面谈谈如何让线程进入以上几种状态:
1. NEW, 这个最简单了,
* 多线程在NEW状态是指线程刚创建,还没有在其上调用start()方法。{新建(new)}
static void NEW() {
Thread t = new Thread();
System.out.println(t.getState());
2. RUNNABLE, 也简单, 让一个thread start, 同时代码里面不要sleep或者wait等
* RUNNABLE 线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。
private static void RUNNABLE() {
Thread t = new Thread() {
public void run() {
for (int i = 0; i & Integer.MAX_VALUE; i++) {
System.out.println(i);
t.start();
3. BLOCKED, 这个就必须至少两个线程以上, 然后互相等待synchronized 块
* 阻塞(block):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu
* timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice
* 转到运行(running)状态。阻塞的情况分三种:
* (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
* (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock
* pool)中。
* (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long
* ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
private static void BLOCKED() {
final Object lock = new Object();
Runnable run = new Runnable() {
public void run() {
for (int i = 0; i & Integer.MAX_VALUE; i++) {
synchronized (lock) {
System.out.println(i);
Thread t1 = new Thread(run);
t1.setName("t1");
Thread t2 = new Thread(run);
t2.setName("t2");
t1.start();
t2.start();
这时候, 一个在RUNNABLE, 另一个就会在BLOCKED (等待另一个线程的 System.out.println.. 这是个IO操作, 属于系统资源, 不会造成WAITING等)
4. WAITING, 这个需要用到生产者消费者模型, 当生产者生产过慢的时候, 消费者就会等待生产者的下一次notify
* WAITING() 处于这个状态的线程,说明正在无期限地等待其他线程的某些特定动作
private static void WAITING() {
final Object lock = new Object();
Thread t1 = new Thread() {
public void run() {
int i = 0;
while (true) {
synchronized (lock) {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(i++);
Thread t2 = new Thread() {
public void run() {
while (true) {
synchronized (lock) {
for (int i = 0; i & 10000; i++) {
System.out.println(i);
lock.notifyAll();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
5. TIMED_WAITING, 这个仅需要在4的基础上, 在wait方法加上一个时间参数进行限制就OK了.
把4中的synchronized 块改成如下就可以了.
synchronized (lock) {
lock.wait(60 * 1000L);
} catch (InterruptedException e) {
System. out .println(i++);
另外看stack的输出,
他叫 TIMED_WAITING(on
object monitor) , 说明括号后面还有其他的情况, 比如sleep, 我们直接把t2的for循环改成sleep试试:
synchronized (lock) {
sleep(30 * 1000L);
} catch (InterruptedException e) {
lock.notifyAll();
看到了吧, t2的state是 TIMED_WAITING( sleeping),
而t1依然是on object monitor , 因为t1还是wait在等待t2 notify, 而t2是自己sleep
另外, join操作也是进入 on object monitor
6. TERMINATED, 这个状态只要线程结束了run方法, 就会进入了…
private static void TERMINATED()
Thread t1 = new Thread();
t1.start();
System. out.println(t1.getState());
Thread. sleep(1000L);
} catch (InterruptedException e) {
System. out.println(t1.getState());
TERMINATED
由于线程的start方法是异步启动的, 所以在其执行后立即获取状态有可能才刚进入RUN方法且还未执行完毕
废话了这么多, 了解线程的状态究竟有什么用?
所以说这是个钓鱼贴么…
好吧, 一句话, 在找到系统中的潜在性能瓶颈有作用.
当java系统运行慢的时候, 我们想到的应该先找到性能的瓶颈, 而jstack等工具, 通过jvm当前的stack可以看到当前整个vm所有线程的状态, 当我们看到一个线程状态经常处于
WAITING 或者 BLOCKED的时候, 要小心了, 他可能在等待资源经常没有得到释放(当然, 线程池的调度用的也是各种队列各种锁, 要区分一下, 比如下图)
这是个经典的并发包里面的线程池, 其调度队列用的是LinkedBlockingQueue, 执行take的时候会block住, 等待下一个任务进入队列中, 然后进入执行, 这种理论上不是系统的性能瓶颈, 找瓶颈一般先找自己的代码stack,再去排查那些开源的组件/JDK的问题
排查问题的几个思路:
0. 如何跟踪一个线程?
看到上面的stack输出没有, 第一行是内容是 threadName priority tid nid desc
更过跟踪tid, nid 都可以唯一找到该线程.
1. 发现有线程进入BLOCK, 而且持续好久, 这说明性能瓶颈存在于synchronized块中, 因为他一直block住, 进不去, 说明另一个线程一直没有处理好, 也就这个synchronized块中处理速度比较慢, 然后再深入查看. 当然也有可能同时block的线程太多, 排队太久造成.
2. 发现有线程进入WAITING, 而且持续好久, 说明性能瓶颈存在于触发notify的那段逻辑. 当然还有就是同时WAITING的线程过多, 老是等不到释放.
3. 线程进入TIME_WAITING 状态且持续好久的, 跟2的排查方式一样.
上面的黑底白字截图都是通过jstack打印出来的, 可以直接定位到你想知道的线程的执行栈, 这对java性能瓶颈的分析是有极大作用的.
NOTE: 上面所有代码都是为了跟踪线程的状态而写的, 千万不要在线上应用中这么写…
作者:linzhaojie525 发表于
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
先看一下java线程运行时各个阶段的运行状态
java实现多线程有两种方法
1、继承Thread类
2、实现Runnable接口
这两种方法的共同点:
不论用哪种方法,都必须用Thread(如果是Thead子类就用它本身)产生线程,然后再调用start()方法。
两种方法的不同点:
1、继承Thread类有一个缺点就是单继承,而实现Runnable接口则弥补了它的缺点,可以实现多继承
2、继承Thread类必须如果产生Runnable实例对象,就必须产生多个Runnable实例对象,然后再用Thread产生多个线程;而实现Runnable接口,只需要建立一个实现这个类的实例,然后用这一个实例对象产生多个线程。即实现了资源的共享性
基于以上两点所以建议用第二种方法
下面用例子来做说明
package com.dr.runnable1;//一个类只要继承了Thread类,则此类就是多线程类class MyThread extends Thread{
public MyThread(String name)
this.name=
//如果要使用多线程,则必须有一个方法的主体
public void run()
//打印输出
for(int i=0;i&10;i++)
System.out.println(this.name+"-----&运行、、、、");
}}public class Demo1 {
public static void main(String args[])
{//第一种方法
Runnable r1=new MyThread("线程A");
Runnable r2=new MyThread("线程B");
Runnable r3=new MyThread("线程C");
Thread t1=new Thread(r1);
Thread t2=new Thread(r2);
Thread t3=new Thread(r3);
t1.start();
t2.start();
t3.start();
mt1.run();//线程执行,使用start方法//
mt2.run();//
mt3.run();//第二种方法//
MyThread mt1=new MyThread("线程A");//
MyThread mt2=new MyThread("线程B");//
MyThread mt3=new MyThread("线程C");//
mt1.start();//
mt1.start();//线程只能启动一次//
mt2.start();//
mt3.start();
程序的运行结果是:
这是继承了Thread类,第一种方法产生多个Runnable实例对象,然后用Thread产生多个线程
第二种方法,因为这个类已经继承了Thread类,所以就可以直接利用它本身产生多个线程
package com.dr.runnable1;class MyThread1 implements Runnable{
int ticket=10;
public void run()
for(int i=0;i&500;i++)
if(this.ticket&0)
System.out.println("卖票-----&"+(this.ticket--));
}}public class Demo2 {
public static void main(String args[])
MyThread1 mt=new MyThread1();
Thread t1=new Thread(mt);
Thread t2=new Thread(mt);
Thread t3=new Thread(mt);
t1.start();
t2.start();
t3.start();
程序运行结果:
这个程序是实现Runnable了,产生一类的实例对象,然后用Thread产生多个线程。
作者:linzhaojie525 发表于
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
作者:榴莲艺声
链接:https://www.zhihu.com/question//answer/
来源:知乎
一、关于进程和线程,首先从定义上理解就有所不同
1、进程是什么?
是具有一定独立功能的程序、它是系统进行资源分配和调度的一个独立单位,重点在系统调度和单独的单位,也就是说进程是可以独 立运行的一段程序。
2、线程又是什么?线程进程的一个实体,是CPU调度和分派的基本单位,他是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源。
在运行时,只是暂用一些计数器、寄存器和栈 。
二、他们之间的关系
1、一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程(通常说的主线程)。
2、资源分配给进程,同一进程的所有线程共享该进程的所有资源。
3、线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
4、处理机分给线程,即真正在处理机上运行的是线程。
5、线程是指进程内的一个执行单元,也是进程内的可调度实体。
三、从三个角度来剖析二者之间的区别
1、调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
2、并发性:不仅进程之间。可以并发执行,同一个进程的多个线程之间也可以并发执行
3、拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
四,1,单线程:程序中只存在一个线程,实际上主方法就是一个主线程;
2,多线程:多线程是一个程序运行多个任务;多线程的目的就是更好的使用CPU的资源。
作者:linzhaojie525 发表于
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
一、List回顾
序列(List),有序的Collection,正如它的名字一样,是一个有序的元素列表。确切的讲,列表通常允许满足&e1.equals(e2)&的元素对&e1&和&e2,并且如果列表本身允许
null 元素的话,通常它们允许多个 null 元素。实现List的有:ArrayList、LinkedList、Vector、Stack等。值得一提的是,Vector在JDK1.1的时候就有了,而List在JDK1.2的时候出现,待会我们会聊到ArrayList和Vector的区别。
二、ArrayList vs. Vector
ArrayList是一个可调整大小的数组实现的序列。随着元素增加,其大小会动态的增加。此类在Iterator或ListIterator迭代中,调用容器自身的remove和add方法进行修改,会抛出ConcurrentModificationException并发修改异常。
注意,此实现不是同步的。如果多个线程同时访问一个&ArrayList&实例,而其中至少一个线程从结构上修改了列表,那么它必须&保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用&Collections.synchronizedList&方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:
list = Collections.synchronizedList(new
ArrayList(...));
下面演示下相关ArrayList例子。
ArrayList基本方法代码:
@SuppressWarnings({
&rawtypes&, &unchecked& })
&&&&public
static void listMethods()
&&&&&&&&List
a1 = new ArrayList&String&();
&&&&&&&&a1.add(&List01&);
&&&&&&&&a1.add(&List03&);
&&&&&&&&a1.add(&List04&);
&&&&&&&&System.out.print(&原来集合:\n\t&+a1+&\n&);
&&&&&&&&a1.add(1,&List02&);
&&&&&&&&System.out.print(&指定角标1插入:\n\t&+a1+&\n&);
&&&&&&&&a1.remove(2);
&&&&&&&&System.out.print(&指定角标2删除:\n\t&+a1+&\n&);
&&&&&&&&System.out.print(&指定角标2查询:\n\t&+a1.get(2)+&\n&);
&&&&&&&&Iterator
i1 = a1.iterator();
&&&&&&&&System.out.println(&用迭代器查询全部元素:&);
&&&&&&&&while
(i1.hasNext())
&&&&&&&&&&&&System.out.print(i1.next()+&,&);
可以从控制台可以看出:
原来集合:
&&&&[List01,
List03, List04]
指定角标1插入:
&&&&[List01,
List02, List03, List04]
指定角标2删除:
&&&&[List01,
List02, List04]
指定角标2查询:
&&&&List04
用迭代器查询全部元素:
List01,List02,List04
在上面我们可以根据角标来增加(add)、删除(remove)、获取(get)列表里面元素。ArrayList提供了Iterator迭代器来遍历序列。值得注意的是,迭代器的就相当于一个指针指向角标,next()方法就相当于指针往后移一位。所以切记,用迭代器中一次循环用一次next()。
下面演示下在ConcurrentModificationException的出现,及处理方案。泥瓦匠用Iterator演示这个异常的出现:
iteratorTest()
ArrayList&String&();
a1.add(&List01&);
a1.add(&List02&);
a1.add(&List04&);
a1.add(&List05&);
i1 = a1.iterator();
(i1.hasNext())
obj = i1.next();
(obj.equals(&List02&))
a1.add(&List03&);
System.out.print(&集合:\n\t&+a1+&\n&);
运行,我们可以在控制台看到:
怎么解决的,先看清楚这个问题。问题描述很清楚,在创建迭代器之后,除非通过迭代器自身的&remove&或&add&方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出&ConcurrentModificationException。
因此我们应该这样修改代码,用ListIterator迭代器提供方法,:
@SuppressWarnings({
&unchecked&, &rawtypes& })
&&&&public
static void listIterator()
&&&&&&&&List
a1 = new ArrayList&String&();
&&&&&&&&a1.add(&List01&);
&&&&&&&&a1.add(&List&);
&&&&&&&&a1.add(&List03&);
&&&&&&&&a1.add(&List04&);
&&&&&&&&ListIterator
l1 = a1.listIterator();
&&&&&&&&while
(l1.hasNext())
&&&&&&&&&&&&Object
obj = l1.next();
&&&&&&&&&&&&if
(obj.equals(&List&))
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&l1.remove();
&&&&&&&&&&&&&&&&l1.add(&List02&);
&&&&&&&&&&&&}
&&&&&&&&System.out.print(&集合:\n\t&+a1+&\n&);
运行下,我们可以看到:
&&&&[List01,
List02, List03, List04]
这样,我们成功解决了这个并发修改异常。把其中‘List’元素删除,新增了一个‘List02’的元素。
Vector非常类似ArrayList。早在JDK1.1的时候就出现了,以前没有所谓的List接口,现在此类被改进为实现List接口。但与新的Collection不同的是,Vector是同步的。泥瓦匠想说的是Vector,在像查询的性能上会比ArrayList开销大。下面演示下Vector的基本例子:
@SuppressWarnings({
&unchecked&, &rawtypes& })
&&&&public
static void vectorMethods()
&&&&&&&&Vector
v1 = new Vector&String&();
&&&&&&&&v1.add(&Vector001&);
&&&&&&&&v1.add(&Vector002&);
&&&&&&&&v1.add(&Vector003&);
&&&&&&&&v1.add(&Vector004&);
&&&&&&&&v1.add(&Vector005&);
&&&&&&&&Enumeration
e1 =v1.elements();
&&&&&&&&while
(e1.hasMoreElements())
&&&&&&&&&&&&Object
object = e1.nextElement();
&&&&&&&&&&&&System.out.println(object);
从方法上看几乎没差别,同样注意的是:此接口的功能与 Iterator 接口的功能是重复的。此外,Iterator 接口添加了一个可选的移除操作,并使用较短的方法名。新的实现应该优先考虑使用 Iterator 接口而不是 Enumeration 接口。
三、LinkedList及其与ArrayList性能比
LinkedList与ArrayList一样实现List接口,LinkedList是List接口链表的实现。基于链表实现的方式使得LinkedList在插入和删除时更优于ArrayList,而随机访问则比ArrayList逊色些。LinkedList实现所有可选的列表操作,并允许所有的元素包括null。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
LinkedList和ArrayList的方法时间复杂度总结如下图所示。
表中,添加add()指添加元素的方法,remove()是指除去(int index)角标。ArrayList具有O(N)的任意指数时间复杂度的添加/删除,但O(1)的操作列表的末尾。链表的O(n)的任意指数时间复杂度的添加/删除,但O(1)操作端/列表的开始。
泥瓦匠用代码验证下这个结论:
static void testPerBtwnArlAndLkl()
&&&&&&&&ArrayList&Integer&
arrayList&& = new ArrayList&Integer&();
&&&&&&&&LinkedList&Integer&
linkedList = new LinkedList&Integer&();
&&&&&&&&&&&&&&&&&
&&&&&&&&//
ArrayList add
&&&&&&&&long
startTime& = System.nanoTime();
&&&&&&&&long
&&&&&&&&long
&&&&&&&&&&
&&&&&&&&for
(int i = 0; i & 100000;
i++) {
&&&&&&&&&&&&arrayList.add(i);
&&&&&&&&endTime
= System.nanoTime();
&&&&&&&&duration
&&&&&&&&System.out.println(&ArrayList
add:& & + duration);
&&&&&&&&&&
&&&&&&&&//
LinkedList add
&&&&&&&&startTime
= System.nanoTime();
&&&&&&&&&&
&&&&&&&&for
i & 100000; i++) {
&&&&&&&&&&&&linkedList.add(i);
&&&&&&&&endTime
= System.nanoTime();
&&&&&&&&duration
&&&&&&&&System.out.println(&LinkedList
add: & + duration);
&&&&&&&&&&
&&&&&&&&//
ArrayList get
&&&&&&&&startTime
= System.nanoTime();
&&&&&&&&&&
&&&&&&&&for
i & 10000; i++) {
&&&&&&&&&&&&arrayList.get(i);
&&&&&&&&endTime
= System.nanoTime();
&&&&&&&&duration
&&&&&&&&System.out.println(&ArrayList
get:& & + duration);
&&&&&&&&&&
&&&&&&&&//
LinkedList get
&&&&&&&&startTime
= System.nanoTime();
&&&&&&&&&&
&&&&&&&&for
i & 10000; i++) {
&&&&&&&&&&&&linkedList.get(i);
&&&&&&&&endTime
= System.nanoTime();
&&&&&&&&duration
&&&&&&&&System.out.println(&LinkedList
get: & + duration);
&&&&&&&&&&
&&&&&&&&//
ArrayList remove
&&&&&&&&startTime
= System.nanoTime();
&&&&&&&&&&
&&&&&&&&for
i &=0; i--) {
&&&&&&&&&&&&arrayList.remove(i);
&&&&&&&&endTime
= System.nanoTime();
&&&&&&&&duration
= endTime - startT
&&&&&&&&System.out.println(&ArrayList
remove:& & + duration);
&&&&&&&&&&
&&&&&&&&//
LinkedList remove
&&&&&&&&startTime
= System.nanoTime();
&&&&&&&&&&
&&&&&&&&for
(int i = 9999; i &=0; i--) {
&&&&&&&&&&&&linkedList.remove(i);
&&&&&&&&endTime
= System.nanoTime();
&&&&&&&&duration
= endTime - startT
&&&&&&&&System.out.println(&LinkedList
remove: & + duration);
控制台输出如下:
LinkedList
get:& 1304593
LinkedList
LinkedList
对比下的话,其性能差距很明显。LinkedList在添加和删除中性能快,但在获取中性能差。从复杂度和测试结果,我们应该懂得平时在添加或者删除操作频繁的地方,选择LinkedList时考虑:
1、没有大量的元素的随机访问
2、添加/删除操作
自然我下面用LinedList实现一个数据结构–栈。泥瓦匠留给大家LinkedList的一些方法自己消化下。
com.sedion.bysocket.
java.util.LinkedL
用LinkedList实现栈
队列和栈区别:队列先进先出,栈先进后出。
class Stack&T&
&&&&private
LinkedList&T&
storage = new LinkedList&T&();
&&&&public
void push(T v)
&&&&&&&&storage.addFirst(v);
出栈,但不删除 */
&&&&public
&&&&&&&&return
storage.getFirst();
出栈,删除 */
&&&&public
&&&&&&&&return
storage.removeFirst();
栈是否为空 */
&&&&public
boolean empty()
&&&&&&&&return
storage.isEmpty();
输出栈元素 */
&&&&public
String toString()
&&&&&&&&return
storage.toString();
&&&&public
static void main(String[] args)
&&&&&&&&Stack
stack=new Stack&String&();
&&&&&&&&stack.push(&a&);
&&&&&&&&stack.push(&b&);
&&&&&&&&stack.push(&c&);
&&&&&&&&System.out.println(stack.toString());
&&&&&&&&Object
obj=stack.peek();
&&&&&&&&System.out.println(obj+&--&+stack.toString());
&&&&&&&&obj=stack.pop();
&&&&&&&&System.out.println(obj+&--&+stack.toString());
&&&&&&&&System.out.println(stack.empty());
泥瓦匠总结如下:
Vector和ArrayList
1、vector是线程同步的,所以它也是线程安全的,而arraylist是线程异步的,是不安全的。
2、记住并发修改异常 java.util.ConcurrentModificationException ,优先考虑ArrayList,除非你在使用多线程所需。
Aarraylist和Linkedlist
1、对于随机访问get和set,ArrayList觉得优于LinkedList,LinkedList要移动指针。
2、于新增和删除操作add和remove,LinedList比较占优势,ArrayList要移动数据。
单条数据插入或删除,ArrayList的速度反而优于LinkedList.原因是:LinkedList的数据结构是三个对象,组大小恰当就会比链表快吧,直接赋值就完了,不用再设置前后指针的值。
若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。
作者:linzhaojie525 发表于
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
1.6API文档(中文)的下载地址:
作者:linzhaojie525 发表于
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
一:如果出现下面的错误信息,如果你的项目是Maven结构的,那么一般都是你的项目的Maven Dependencies没有添加到项目的编译路径下:
信息: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files\Java\jre1.8.0_40\C:\Windows\Sun\Java\C:\Windows\system32;C:\WC:/Program Files/Java/jre1.8.0_40/bin/C:/Program Files/Java/jre1.8.0_40/C:/Program Files/Java/jre1.8.0_40/lib/i386;C:\ProgramData\Oracle\Java\C:\Program Files\Common Files\NetSF:\oracle\product\10.2.0\db_2\C:\Windows\system32;C:\WC:\Windows\system32\C:\Windows\system32\windowspowershell\v1.0\;C:\Program Files\Java\jdk1.8.0_40\C:\Program Files\Java\jdk1.8.0_40\D:\Program Files\apache-ant-1.8.2\D:\Program Files\apache-maven-3.0.5\c:\program files\ibm\gsk8\C:\Python27;C:\Program Files\TortoiseSVN\.;D:\android-sdk-windows\platform-D:\android-sdk-windows\d:\Program Files\SSH Communications Security\SSH Secure SC:\Program Files\MySQL\MySQL Server 5.0\D:\Program Files\apache-maven-3.0.5;D:\eclipse-jee-luna-SR2-win32\;.
五月 10, :18 下午 org.apache.tomcat.util.digester.SetPropertiesRule begin
警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.j2ee.server:sample-web-test' did not find a matching property.
五月 10, :19 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["http-bio-8080"]
五月 10, :19 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["ajp-bio-8009"]
五月 10, :19 下午 org.apache.catalina.startup.Catalina load
信息: Initialization processed in 849 ms
五月 10, :19 下午 org.apache.catalina.core.StandardService startInternal
信息: Starting service Catalina
五月 10, :19 下午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet Engine: Apache Tomcat/7.0.52
五月 10, :19 下午 org.apache.catalina.core.StandardContext listenerStart
严重: Error configuring application listener of class org.springframework.web.util.IntrospectorCleanupListener
java.lang.ClassNotFoundException: org.springframework.web.util.IntrospectorCleanupListener
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1718)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1569)
at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:529)
at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:511)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:139)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4888)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
五月 10, :19 下午 org.apache.catalina.core.StandardContext listenerStart
严重: Skipped installing application listeners due to previous error(s)
五月 10, :19 下午 org.apache.catalina.core.StandardContext startInternal
严重: Error listenerStart
五月 10, :20 下午 org.apache.catalina.util.SessionIdGenerator createSecureRandom
信息: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [344] milliseconds.
五月 10, :20 下午 org.apache.catalina.core.StandardContext startInternal
严重: Context [/sample-web-test] startup failed due to previous errors
五月 10, :20 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory D:\apache-tomcat-7.0.52\webapps\docs
五月 10, :20 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory D:\apache-tomcat-7.0.52\webapps\examples
五月 10, :20 下午 org.apache.catalina.core.ApplicationContext log
信息: ContextListener: contextInitialized()
五月 10, :20 下午 org.apache.catalina.core.ApplicationContext log
信息: SessionListener: contextInitialized()
五月 10, :20 下午 org.apache.catalina.core.ApplicationContext log
信息: ContextListener: attributeAdded('org.apache.jasper.compiler.TldLocationsCache', 'org.apache.jasper.compiler.TldLocationsCache@1e59f14')
五月 10, :20 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory D:\apache-tomcat-7.0.52\webapps\host-manager
五月 10, :21 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory D:\apache-tomcat-7.0.52\webapps\manager
五月 10, :21 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory D:\apache-tomcat-7.0.52\webapps\ROOT
五月 10, :21 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-bio-8080"]
五月 10, :21 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["ajp-bio-8009"]
五月 10, :21 下午 org.apache.catalina.startup.Catalina start
信息: Server startup in 2028 ms
二:解决办法:
①选中项目-&右键Properties-&选择Deployment Assemby-&选择Add-&选中 Build Path Entries-&Next-&选择Maven
Dependencies-&Finish-&Apply-&OK
作者:linzhaojie525 发表于
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
Multiple annotations found at this line:- schema_reference.4: Failed to read schema document 'http://www.springframework.org/schema/beans/spring-beans-3.1.xsd', because 1) could n 2) the docume 3) the root element of the document is not &xsd:schema&.
- cvc-elt.1: Cannot find the declaration of element 'beans'.
如上,在使用eclipse构建基于maven的springmvc 工程时,报上面的错误
如下为的配置:
经百度和搜索,发现原来是因为我在maven pom.xml 中依赖的spring的版本和配置不匹配导致的。
我在pom.xml中使用的是
&spring.version&4.0.9.RELEASE&/spring.version&
使用的是 4.0 版本的,但是上面是3.1版本的
把上面截图中的spring配置改为4.0 ,同时做一次clean操作,问题解决
作者:linzhaojie525 发表于
https://blog.csdn.net/linzhaojie525/article/details/
阅读:1166
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
以前都是使用国外的maven仓库,但是下载太慢了,经过群里介绍知道了阿里云的maven仓库,下载速度超快,朋友们可以配置下体验一下
仓库地址:
在maven的settings.xml文件里的mirrors节点,添加如下子节点:
&id&nexus-aliyun&/id&
&mirrorOf&central&/mirrorOf&
&name&Nexus aliyun&/name&
&url&http://maven.aliyun.com/nexus/content/groups/public&/url&
或者直接在profiles-&profile-&repositories节点,添加如下子节点:
&repository&
&id&nexus-aliyun&/id&
&name&Nexus aliyun&/name&
&layout&default&/layout&
&url&http://maven.aliyun.com/nexus/content/groups/public&/url&
&snapshots&
&enabled&false&/enabled&
&/snapshots&
&releases&
&enabled&true&/enabled&
&/releases&
&/repository&
settings文件的路径
settings.xml的默认路径就:个人目录/.m2/settings.xml
windowns: C:\Users\你的用户名\.m2\settings.xml
linux: /home/你的用户名/.m2/settin
作者:linzhaojie525 发表于
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
https://blog.csdn.net/linzhaojie525/article/details/
linzhaojie525
一、多线程的优缺点
多线程的优点:
1)资源利用率更好
2)程序设计在某些情况下更简单
3)程序响应更快
多线程的代价:
1)设计更复杂
虽然有一些多线程应用程序比单线程的应用程序要简单,但其他的一般都更复杂。在多线程访问共享数据的时候,这部分代码需要特别的注意。线程之间的交互往往非常复杂。不正确的线程同步产生的错误非常难以被发现,并且重现以修复。
2)上下文切换的开销
当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开始执行。这种切换称为“上下文切换”(“context switch”)。CPU会在一个上下文中执行一个线程,然后切换到另外一个上下文中执行另外一个线程。上下文切换并不廉价。如果没有必要,应该减少上下文切换的发生。
二、创建java多线程
1、创建Thread的子类
创建Thread子类的一个实例并重写run方法,run方法会在调用start()方法之后被执行。例子如下:
public class MyThread extends Thread {
public void run(){
System.out.println("MyThread running");
MyThread myThread = new MyThread();
myTread.start();
也可以如下创建一个Thread的匿名子类:
Thread thread = new Thread(){
public void run(){
System.out.println("Thread Running");
thread.start();
2、实现Runnable接口
第二种编写线程执行代码的方式是新建一个实现了java.lang.Runnable接口的类的实例,实例中的方法可以被线程调用。下面给出例子:
public class MyRunnable implements Runnable {
public void run(){
System.out.println("MyRunnable running");
Thread thread = new Thread(new MyRunnable());
thread.start();
同样,也可以创建一个实现了Runnable接口的匿名类,如下所示:
Runnable myRunnable = new Runnable(){
public void run(){
System.out.println("Runnable running");
Thread thread = new Thread(myRunnable);
thread.start();
三、线程安全
在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源。如同一内存区(变量,数组,或对象)、系统(数据库,web services等)或文件。实际上,这些问题只有在一或多个线程向这些资源做了写操作时才有可能发生,只要资源没有发生变化,多个线程读取相同的资源就是安全的。
当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。导致竞态条件发生的代码区称作临界区。
如果一个资源的创建,使用,销毁都在同一个线程内完成,且永远不会脱离该线程的控制,则该资源的使用就是线程安全的。
四、java同步块
Java中的同步块用synchronized标记。同步块在Java中是同步在某个对象上。所有同步在一个对象上的同步块在同时只能被一个线程进入并执行操作。所有其他等待进入该同步块的线程将被阻塞,直到执行该同步块中的线程退出。
有四种不同的同步块:
实例方法静态方法实例方法中的同步块静态方法中的同步块
实例方法同步:
public synchronized void add(int value){
this.count +=
Java实例方法同步是同步在拥有该方法的对象上。这样,每个实例其方法同步都同步在不同的对象上,即该方法所属的实例。只有一个线程能够在实例方法同步块中运行。如果有多个实例存在,那么一个线程一次可以在一个实例同步块中执行操作。一个实例一个线程。
静态方法同步:
public static synchronized void add(int value){
静态方法的同步是指同步在该方法所在的类对象上。因为在Java虚拟机中一个类只能对应一个类对象,所以同时只允许一个线程执行同一个类中的静态同步方法。
实例方法中的同步块:
public void add(int value){
synchronized(this){
this.count +=
注意Java同步块构造器用括号将对象括起来。在上例中,使用了“this”,即为调用add方法的实例本身。在同步构造器中用括号括起来的对象叫做监视器对象。上述代码使用监视器对象同步,同步实例方法使用调用方法本身的实例作为监视器对象。一次只有一个线程能够在同步于同一个监视器对象的Java方法内执行。
下面两个例子都同步他们所调用的实例对象上,因此他们在同步的执行效果上是等效的。
public class MyClass {
public synchronized void log1(String msg1, String msg2){
log.writeln(msg1);
log.writeln(msg2);
public void log2(String msg1, String msg2){
synchronized(this){
log.writeln(msg1);
log.writeln(msg2);
静态方法中的同步块:
public class MyClass {
public static synchronized void log1(String msg1, String msg2){
log.writeln(msg1);
log.writeln(msg2);
public static void log2(String msg1, String msg2){
synchronized(MyClass.class){
log.writeln(msg1);
log.writeln(msg2);
这两个方法不允许同时被线程访问。如果第二个同步块不是同步在MyClass.class这个对象上。那么这两个方法可以同时被线程访问。
五、java线程通信
线程通信的目标是使线程间能够互相发送信号。另一方面,线程通信使线程能够等待其他线程的信号。
Java有一个内建的等待机制来允许线程在等待信号的时候变为非运行状态。java.lang.Object 类定义了三个方法,wait()、notify()和notifyAll()来实现这个等待机制。
一个线程一旦调用了任意对象的wait()方法,就会变为非运行状态,直到另一个线程调用了同一个对象的notify()方法。为了调用wait()或者notify(),线程必须先获得那个对象的锁。也就是说,线程必须在同步块里调用wait()或者notify()。
以下为一个使用了wait()和notify()实现的线程间通信的共享对象:
public class MyWaitNotify{
MonitorObject myMonitorObject = new MonitorObject();
boolean wasSignalled = false;
public void doWait(){
synchronized(myMonitorObject){
while(!wasSignalled){
myMonitorObject.wait();
} catch(InterruptedException e){...}
//clear signal and continue running.
wasSignalled = false;
public void doNotify(){
synchronized(myMonitorObject){
wasSignalled = true;
myMonitorObject.notify();
注意以下几点:
1、不管是等待线程还是唤醒线程都在同步块里调用wait()和notify()。这是强制性的!一个线程如果没有持有对象锁,将不能调用wait(),notify()或者notifyAll()。否则,会抛出IllegalMonitorStateException异常。
2、一旦线程调用了wait()方法,它就释放了所持有的监视器对象上的锁。这将允许其他线程也可以调用wait()或者notify()。
3、为了避免丢失信号,必须把它们保存在信号类里。如上面的wasSignalled变量。
4、假唤醒:由于莫名其妙的原因,线程有可能在没有调用过notify()和notifyAll()的情况下醒来。这就是所谓的假唤醒(spurious wakeups)。为了防止假唤醒,保存信号的成员变量将在一个while循环里接受检查,而不是在if表达式里。这样的一个while循环叫做自旋锁。
5、不要在字符串常量或全局对象中调用wait()。即上面MonitorObject不能是字符串常量或是全局对象。每一个MyWaitNotify的实例都拥有一个属于自己的监视器对象,而不是在空字符串上调用wait()/notify()。
六、java中的锁
自Java 5开始,java.util.concurrent.locks包中包含了一些锁的实现,因此你不用去实现自己的锁了。
常用的一些锁:
java.util.concurrent.locks.L
java.util.concurrent.locks.ReentrantL
java.util.concurrent.locks.ReadWriteL
java.util.concurrent.locks.ReentrantReadWriteL
一个可重入锁(reentrant lock)的简单实现:
public class Lock {
boolean isLocked = false;
lockedBy = null;
int lockedCount = 0;
public synchronized void lock() throws InterruptedException{
Thread callingThread = Thread.currentThread();
while(isLocked && lockedBy != callingThread){
isLocked = true;
lockedCount++;
lockedBy = callingT
public synchronized void unlock(){
if(Thread.currentThread() == this.lockedBy){
lockedCount--;
if(lockedCount == 0){
isLocked = false;
注意的一点:在finally语句中调用unlock()
lock.lock();
//do critical section code, which may throw exception
} finally {
lock.unlock();
七、java中其他同步方法
信号量(Semaphore):java.util.concurrent.Semaphore
阻塞队列(Blocking Queue):java.util.concurrent.BlockingQueue
public class BlockingQueue {
private List queue = new LinkedList();
private int limit = 10;
public BlockingQueue(int limit) {
this.limit =
public synchronized void enqueue(Object item) throws InterruptedException {
while (this.queue.size() == this.limit) {
if (this.queue.size() == 0) {
notifyAll();
this.queue.add(item);
public synchronized Object dequeue() throws InterruptedException {
while (this.queue.size() == 0) {
if (this.queue.size() == this.limit) {
notifyAll();
return this.queue.remove(0);
八、java中的线程池
Java通过Executors提供四种线程池,分别为:
newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
newScheduledThreadPool
创建一个大小无限制的线程池。此线程池支持定时以及周期性执行任务。
newSingleThreadExecutor
创建一个单线程的线程池。此线程池支持定时以及周期性执行任务。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
线程池简单用法:
import java.util.concurrent.ExecutorS
import java.util.concurrent.E
public class Main {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i & 10; i++) {
final int index =
cachedThreadPool.execute(new Runnable() {
public void run() {
System.out.println(index);
一、多线程的优缺点
多线程的优点:
1)资源利用率更好
2)程序设计在某些情况下更简单
3)程序响应更快
多线程的代价:
1)设计更复杂
虽然有一些多线程应用程序比单线程的应用程序要简单,但其他的一般都更复杂。在多线程访问共享数据的时候,这部分代码需要特别的注意。线程之间的交互往往非常复杂。不正确的线程同步产生的错误非常难以被发现,并且重现以修复。
2)上下文切换的开销
当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开始执行。这种切换称为“上下文切换”(“context switch”)。CPU会在一个上下文中执行一个线程,然后切换到另外一个上下文中执行另外一个线程。上下文切换并不廉价。如果没有必要,应该减少上下文切换的发生。
二、创建java多线程
1、创建Thread的子类
创建Thread子类的一个实例并重写run方法,run方法会在调用start()方法之后被执行。例子如下:
public class MyThread extends Thread {
public void run(){
System.out.println("MyThread running");
MyThread myThread = new MyThread();
myTread.start();
也可以如下创建一个Thread的匿名子类:
Thread thread = new Thread(){
public void run(){
System.out.println("Thread Running");
thread.st

我要回帖

更多关于 单位信息编码是什么 的文章

 

随机推荐