介绍

线程是操作系统的最小单位,一个进程可以创建多个线程。

线程有五种状态,分别是新建、就绪、运行、阻塞、死亡状态。

多线程可以提高执行效率,但是如果单线程可以完成的任务,使用多线程反而会增加不必要的开销,降低效率。例如将某个数加一百次,使用多线程反而会比单线程耗费的时间多。

创建线程

java创建线程有两种方法

  • 继承Thread,重写run函数,调用start方法
package com.thread;

public class ExtendTreadTest extends Thread {
int i = 0; public void run()
{
for(;i<100;i++){
System.out.println(getName()+" "+i);
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} }
} public static void main(String[] args) {
new ExtendTreadTest().start();
new ExtendTreadTest().start();
}
}
  • 实现Runnable接口,重写run函数,调用start方法
package com.thread;

import static java.lang.Thread.sleep;

public class ImplementRunnable implements Runnable {
int i = 0; public void run()
{
for(;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} }
} public static void main(String[] args) {
Thread thread = new Thread(new ImplementRunnable(), "test1");
thread.start();
Thread thread1 = new Thread(new ImplementRunnable(), "test2");
thread1.start();
}
}

JAVA允许继承一个类,并实现多个接口,所以通常应用中实现Runnable比较好。

线程间通信

volatile和synchornized关键字

volatile

volatile 会确保线程在每一次使用变量之前都会从共享内存中读取变量的值,然后然后放入自己的工作内存进行处理,处理完成后将新的值立即同步到内存,在这个期间,可能会有其他的线程也对该变量进行处理。所以volatile并不是线程安全的。

package com.thread;

public class Test {
public volatile int inc = 0; public void increase() {
inc++;
System.out.println(inc);
} public static void main(String[] args) {
final Test test = new Test();
for(int i=0;i<10;i++){
new Thread(){
public void run() {
for(int j=0;j<10000;j++)
test.increase();
};
}.start();
} while(Thread.activeCount()>1) //保证前面的线程都执行完
Thread.yield();
System.out.println(test.inc);
}
}

例如上面的方法,期望的实验结果是100000,但是实际情况偶尔会出现小于100000的情况。是因为volatile没法保证对变量操作的原子性,inc++不是一个原子操作,就会导致结果出现问题。

解决的办法有使用synchronized,锁,以及将变量变为原子操作AtomicInteger。

总体来说,如果能保证对变量的操作是原子性的,那么使用volatile会是一个较好的办法。

参考:http://www.cnblogs.com/dolphin0520/p/3920373.html

synchornized

确保多线程对临界资源的互斥性访问的一种方式是使用synchornized。

例如上面的例子:

    public synchornized void increase() {
inc++;
System.out.println(inc);
}

或者

    public void increase() {
synchornized {
inc++;
System.out.println(inc);
}
}

上面的两个都是对类的对象做同步,而不是对类本身进行同步。每个线程必须共用一个对象才能够达到同步效果。

    public void increase() {
synchornized(Test.class) {
inc++;
System.out.println(inc);
}
}

上面是对类本身进行同步,对于Test类,它只有一个类定义,同时只有一个线程可以访问increase方法。

在JAVA中,任意的一个对象都有自己的监视器,当这个对象由同步块或者同步方法调用的时候,执行的线程必须获取该对象的监视器,然后再进入同步块(方法),否则就会进入一个阻塞队列,等待线程退出监视器。

wait和notify以及notifyAll

1.调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)

2.调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程

3.调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程

package com.thread;

import static java.lang.Thread.sleep;

public class WaitNotify {
static boolean flag = true;
static Object lock = new Object(); public static void main(String[] args) {
Thread waitThread = new Thread(new Wait(), "waitThread");
waitThread.start();
try {
sleep(100);
} catch (Exception err) {
err.printStackTrace();
}
Thread notifyThread = new Thread(new Notify(), "notifyThread");
notifyThread.start(); } static class Wait implements Runnable {
public void run() {
synchronized (lock) {
while (flag) {
try {
System.out.println(Thread.currentThread().getName() + " wait");
lock.wait();
} catch (InterruptedException e) { }
}
}
System.out.println(Thread.currentThread().getName() + " run");
}
} static class Notify implements Runnable {
public void run() {
synchronized (lock) {
try {
System.out.println(Thread.currentThread().getName() + " hold lock");
sleep(1000);
lock.notify();
flag = false; } catch (InterruptedException e) { }
}
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " run");
}
}
}

图中,waitThread首先获取了对象的锁,然后调用对象的wait()方法,从而放弃了锁并进入等待队列WaitQueue中。NotifyThread随后获取了对象的锁,并调用对象的notify方法,将WaitThread从WaitQueue转移到了synchornizedQueue中,然后waitThread转变为了阻塞状态。NotifyThread释放了锁之后,WaitThread再次获得了锁并从wait()方法中返回并继续执行。

参考:JAVA并发编程的艺术

JAVA多线程一的更多相关文章

  1. 深入java多线程一

    涉及到 1.线程的启动(start) 2.线程的暂停(suspend()和resume()) 3.线程的停止(interrupt与异常停止,interrupt与睡眠中停止,stop(),return) ...

  2. java 多线程一

    java 多线程一 java 多线程二 java 多线程三 java 多线程四 java 多线程实现的几种方式: 1.extends Thread 2.implements Runnable 3.im ...

  3. (三十)java多线程一

    我们通常在电脑中打开的应用称作进程,一个应用就是一个进程,而一个进程里边一般包含多个线程. 系统要为每一个进程分配独立的内存空间,而进程里的多个线程共用这些内存. 我们通常所写的main方法就是一个线 ...

  4. Java多线程之ConcurrentSkipListMap深入分析(转)

    Java多线程之ConcurrentSkipListMap深入分析   一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...

  5. 用“逐步排除”的方法定位Java服务线上“系统性”故障(转)

    一.摘要 由于硬件问题.系统资源紧缺或者程序本身的BUG,Java服务在线上不可避免地会出现一些“系统性”故障,比如:服务性能明显下降.部分(或所 有)接口超时或卡死等.其中部分故障隐藏颇深,对运维和 ...

  6. JAVA多线程之wait/notify

    本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...

  7. JAVA多线程之volatile 与 synchronized 的比较

    一,volatile关键字的可见性 要想理解volatile关键字,得先了解下JAVA的内存模型,Java内存模型的抽象示意图如下: 从图中可以看出: ①每个线程都有一个自己的本地内存空间--线程栈空 ...

  8. java多线程之yield,join,wait,sleep的区别

    Java多线程之yield,join,wait,sleep的区别 Java多线程中,经常会遇到yield,join,wait和sleep方法.容易混淆他们的功能及作用.自己仔细研究了下,他们主要的区别 ...

  9. Java多线程之Runnable与Thread

    Java多线程之Thread与Runnable 一.Thread VS Runnable 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类和 ...

随机推荐

  1. asp.net+swfupload 多图片批量上传(附源码下载)

    asp.net的文件上传都是单个文件上传方式,无法执行一次性多张图片批量上传操作,要实现多图片批量上传需要借助于flash,通过flash选取多个图片(文件),然后再通过后端服务进行上传操作. 本次教 ...

  2. jQuery常见面试题(转)

    代码以jQuery 1.83 为例 一 :Q: What is the difference between .get(), [], and .eq()? A: eq返回原生jQuery对象,截取某些 ...

  3. CentOS软件安装目录查找

    注:一般的软件的默认安装目录在/usr/local或者/opt里,可以到那里去找找. 指令名称:whereis 功能介绍:在特定目录中查找符合条件的文件.这些文件的烈性应属于原始代码,二进制文件,或是 ...

  4. PHP 投票练习

    重点:1.进度条的显示2.操作数据库<form action="chuli.php" method="post"> <?php include ...

  5. Linux音频编程指南

    Linux音频编程指南 虽然目前Linux的优势主要体现在网络服务方面,但事实上同样也有着非常丰富的媒体功能,本文就是以多媒体应用中最基本的声音为对象,介绍如何在Linux平台下开发实际的音频应用程序 ...

  6. flex 4 布局样式

    Flex 4 样式与布局 第一篇 Flex 4 与自定义布局(Layout) Flex 4/Spark组件架构的新功能之一是可以定制一个容器的布局而不必改变容器本身.您需要做的就是定义一个自定义布局. ...

  7. OpenMp之sections用法

    section语句是用在sections语句里用来将sections语句里的代码划分成几个不同的段 #pragma omp [parallel] sections [子句] {    #pragma ...

  8. YTU 2616: A代码完善--简易二元运算

    2616: A代码完善--简易二元运算 时间限制: 1 Sec  内存限制: 128 MB 提交: 280  解决: 187 题目描述 注:本题只需要提交填写部分的代码,请按照C++方式提交. 编写二 ...

  9. Java中什么时候使用构造方法

    JAVA是面向对象的语言,面向对象不是这么直接简单,它的设计思想就是要代码重用.即我以前干过类似的事,那么我找出以前可以用到的代码,完成一部分.以前没有的我重新写.这样就有了类.有了类,就是有了可以重 ...

  10. python中的 @ 修饰符

    今天学习廖老师的python教程,碰到了修饰符'@',不太了解,查看了下官方文档. 简单的整理下: @dec2 @dec1 def func(arg1, arg2, ...): pass 等价于 de ...