Java并行程序基础

一、线程的生命周期

其中blocked和waiting的区别:

作者:赵老师
链接:https://www.zhihu.com/question/27654579/answer/128050125
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

假设t1,t2先后两个线程,都执行如下代码:

synchronized(Obj) {
Obj.wait();
}

t1先进,最后在Obj.wait()下卡住,这时java管t1的状态waitting状态
t2后进,直接在第一行就卡住了,这时java叫t2为blocked状态。

请注意,blocked是过去分词,意味着他是被卡住的(无辜啊,全是泪)。因为这段代码只让一条线程运行。同时,jvm是知道怎么结束blocked的,只要别的线程退出这段代码,他就会自动让你进去。也就是说别的线程无需唤醒你,由jvm自动来干

而waiiting是说我调用wait()等函数,主动卡住自己(我在等一个白富美),请jvm在满足某种条件后(白富美发消息让我们晚上见),比如另条线程调用了notify()后,把我唤醒。这个唤醒的责任在于别的线程(白富美)明确的调用一些唤醒函数。

做这样的区分,是jvm出于管理的需要,做了这种区分,比如两个原因的线程放两个队列里管理,如果别的线程运行出了synchronized这段代码,我只需要去blocked队列,放个出来。而某人调用了notify(),我只需要去waitting队列里取个出来。

P.S. 从linux内核来看,这些线程都是等待状态,没区别,区别只在于java的管理需要。通常我们在系统级别说线程的blocked,是说线程操作io,被暂停了,这种线程由linux内核来唤醒(io设备报告数据来了,内核把block的线程放进可运行的进程队列,依次得到处理器时间),而wait是说,等待一个内核mutex对象,另个线程signal这个mutex后,这个线程才可以运行。区别在于由谁唤醒,是操作系统,还是另一个线程,这里倒和java很相似。

 

二、线程的基本操作

一)新建线程

1.方法一:

Thread t=new Thread();
t.start(); //新建一个线程,并在该线程中调用t的run()方法。

2.方法二:

public Class MyThread implements Runnable{

    public static void mian(String[] args){
Thread t2=new Thread(new MyThread());
t2.start();
} @Override
public void run(){
System.out.println("I am runnable man!");
}
}

二)终止线程

1.Thread中的stop()方法会直接终止线程,容易引发线程安全问题已经被弃用。

2.所以我们采用不立即中断线程,而是给目标线程发送通知:“希望你能中断线程的时候中断啦!”。而什么时候中断取决于线程自身。

public void Thread.inerrupt(); //通知线程中断,设置中断标志位。
public boolean Thread.isInterrupted() // 判断线程是否有中断标志位。
public static boolean Thead.interrupted() //判断线程是否有中断标志位,如果有则清除标志位。
public static native void sleep(long millis) throws InterruptedException //如果当前线程被设置了中断标志位,调用sleep方法时,
//就会抛出InterruptedException异常,该异常必须被捕获。

三)wait和notify

两个方法都是Object类中的方法,方法签名为:

public final void wait() throws InterruptException;//线程停止执行转为等待状态,直到其他线程调用了同一实例对象的notify()或notifyAll()方法。
public final native void notify();//随机选择一个线程,将其唤醒。

需要强调的是,这两个方法都不能随便调用,它们必须包含在synchronized语句中。

例如:

public class SimpleWN {

    final static Object obj=new Object();
public static class T1 extends Thread{
@Override
public void run(){
synchronized (obj){
System.out.println(System.currentTimeMillis()+" :T1 start!");
try {
System.out.println(System.currentTimeMillis()+" :wait for object!");
obj.wait();
}catch (Exception e){
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()+" :T1 END!");
}
}
} public static class T2 extends Thread{
@Override
public void run(){
synchronized (obj){
System.out.println(System.currentTimeMillis()+" :T2 start! notify one!"); System.out.println(System.currentTimeMillis()+" :wait for object!");
obj.notify(); System.out.println(System.currentTimeMillis()+" :T2 END!");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} public static void main(String[] args){
Thread t1=new T1();
Thread t2=new T2();
t1.start();
t2.start();
}
}

四)suspend和resume

– suspend()不会释放锁
– 如果加锁发生在resume()之前 ,则死锁发生

五)等待线程结束(join)和谦让(yield)

public final void join() throws InterrupteException //一直阻塞当前线程直到目标线(调用此方法的线程)程执行完毕

public final synchronized void join(long millis) throws InterruptException //超出最大时间会继续执行。

join线程的本质是让调用线程wait()在当前线程对象实例上。因此不要在Thread对象实例上使用wait或者notify等类似的方法,因为这很可能影响系统API的工作。

public static native void yield() //使当前线程让出CPU,与其他线程参与到CPU资源竞争中。

三、守护线程(Deamon)

在后台默默地完成一些系统性的服务,比如垃圾回收线程、 JIT线程就可以理解为守护线程
当一个Java应用内,只有守护线程时,Java虚拟机就会自然退出。

设置为守护线程:

Thread t=new DaemonT();
t.setDaemon(true);
t.start();

四、实现线程同步的基本方法

关键字synchronized的作用是实现线程间的同步,它的工作是对同步的代码加锁,使得每一次只能有一个线程进入同步块。用法:

1)对某个实例对象加锁。

2)对对象的实例方法加锁,相当于对当前实例加锁。

3)对某个类的静态方法加锁,相当于对当前类加锁。

public class MyRunnable implements Runnable{

    static MyRunnable instance=new MyRunnable();
static Object object=new Object();
static int i=0; @Override
public void run() {
for(int j=0;j<1000;j++){
synchronized (object) {
i++;
}
}
} public static void main(String[] args) throws InterruptedException{
Thread t1=new Thread(instance);
Thread t2=new Thread(instance); t1.start();
t2.start();
t1.join();
System.out.println(i); //
t2.join();
System.out.println(i); //
}
}

没问题:

public class MyRunnable implements Runnable{

    static MyRunnable instance=new MyRunnable();
static Object object=new Object();
static int i=0; public static synchronized void increase(){
i++;
} @Override
public void run() {
for(int j=0;j<1000;j++){
increase();
}
} public static void main(String[] args) throws InterruptedException{
Thread t1=new Thread(new MyRunnable());
Thread t2=new Thread(new MyRunnable()); t1.start();
t2.start();
t1.join();
System.out.println(i); //
t2.join();
System.out.println(i); //
}
}

错误的同步方式(自行思考原因):

public class MyRunnable implements Runnable{

    static MyRunnable instance=new MyRunnable();
static Object object=new Object();
static int i=0; public synchronized void increase(){
i++;
} @Override
public void run() {
for(int j=0;j<1000;j++){
increase();
}
} public static void main(String[] args) throws InterruptedException{
Thread t1=new Thread(new MyRunnable());
Thread t2=new Thread(new MyRunnable()); t1.start();
t2.start();
t1.join();
System.out.println(i); //
t2.join();
System.out.println(i); //1747
}
}

五、隐蔽的错误

一)并发下的ArrayList

public class ArrayListTest {

    static ArrayList<Integer> list=new ArrayList<>();
public static class AddThread implements Runnable{
@Override
public void run() {
for(int i=0;i<10000;i++){
list.add(i);
}
}
} public static void main(String[] args) throws InterruptedException{
Thread t1=new Thread(new AddThread());
Thread t2=new Thread(new AddThread()); t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(list.size());
}
/*结果:Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 10
at java.util.ArrayList.add(Unknown Source)
at com.tang.test.ArrayListTest$AddThread.run(ArrayListTest.java:12)
at java.lang.Thread.run(Unknown Source)
10005*/
}

解决办法:Vector代替ArrayList

二)更诡异的HashMap

解决办法:ConcurrentHashMap代替HashMap。

三)错误的加锁

public class BadLock implements Runnable{

    static Integer i=0;
static BadLock instance=new BadLock();
@Override
public void run() {
for(int j=0;j<10000;j++){
synchronized (i) {
i++;
}
}
} public static void main(String[] args) throws InterruptedException{
Thread t1=new Thread(instance);
Thread t2=new Thread(instance);
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println(i);
}
//结果:12862
}

Java并发程序设计(二)Java并行程序基础的更多相关文章

  1. JAVA并行程序基础二

    JAVA并行程序基础二 线程组 当一个系统中,如果线程较多并且功能分配比较明确,可以将相同功能的线程放入同一个线程组里. activeCount()可获得活动线程的总数,由于线程是动态的只能获取一个估 ...

  2. JAVA并行程序基础

    JAVA并行程序基础 一.有关线程你必须知道的事 进程与线程 在等待面向线程设计的计算机结构中,进程是线程的容器.我们都知道,程序是对于指令.数据及其组织形式的描述,而进程是程序的实体. 线程是轻量级 ...

  3. Java并发程序设计(一) 基础概念

    Java并发程序设计(一) 基础概念 一.必须知道的几个概念 一)同步(Synchronous)和异步(Asynchronous) 同步:同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后 ...

  4. JAVA并行程序基础一

    JAVA并行程序基础一 线程的状态 初始线程:线程的基本操作 1. 新建线程 新建线程只需要使用new关键字创建一个线程对象,并且用start() ,线程start()之后会执行run()方法 不要直 ...

  5. Java并发编程二三事

    Java并发编程二三事 转自我的Github 近日重新翻了一下<Java Concurrency in Practice>故以此文记之. 我觉得Java的并发可以从下面三个点去理解: * ...

  6. Java并发(二十二):定时任务ScheduledThreadPoolExecutor

    需要在理解线程池原理的基础上学习定时任务:Java并发(二十一):线程池实现原理 一.先做总结 通过一个简单示例总结: public static void main(String[] args) { ...

  7. 和朱晔一起复习Java并发(二):队列

    和朱晔一起复习Java并发(二):队列 老样子,我们还是从一些例子开始慢慢熟悉各种并发队列.以看小说看故事的心态来学习不会显得那么枯燥而且更容易记忆深刻. 阻塞队列的等待? 阻塞队列最适合做的事情就是 ...

  8. Java并发编程:Java的四种线程池的使用,以及自定义线程工厂

    目录 引言 四种线程池 newCachedThreadPool:可缓存的线程池 newFixedThreadPool:定长线程池 newSingleThreadExecutor:单线程线程池 newS ...

  9. Java并发编程 (二) 并发基础

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.CPU多级缓存-缓存一致性 1.CPU多级缓存 ​ 上图展示的是CPU高级缓存的配置,数据的读取和存 ...

随机推荐

  1. Nginx详解十:Nginx场景实践篇之Nginx静态资源场景配置

    一.静态资源WEB服务 1.静态资源类型:非服务器动态运行生成的文件 2.静态资源服务场景-CDN 假设静态资源存储中心在云南,用户在北京去请求一个文件,那么就会造成一个传输的延时,而如果Nginx同 ...

  2. getComputedStyle()用法详解

    那如果元素即没有在style属性中设置宽高,也没有在样式表中设置宽高,还能用getComputedStyle或currentStyle获取吗?答案是getComputedStyle可以,current ...

  3. 基于“MVC”框架集设计模式,开发用户管理系统!

    MVC----(Model View Controller)设计模型: M:表示业务数据和业务规则.包括DAO(beans).DBHelper(DBHelper),用于封装数据库连接,业务数据库处理. ...

  4. yslow V2 准则详细讲解

    主要有12条:   1. Make fewer HTTP requests 尽可能少的http请求..我们有141个请求(其中15个JS请求,3个CSS请求,47个CSS background ima ...

  5. Scrapy库的安装(windows版)

    这个是需要安装的所有文件 1. wheel pip install wheel 2. lxml http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml 3. P ...

  6. [转] Javascript中理解发布--订阅模式

    发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 现实生活中的发布- ...

  7. Openstack1 云计算与虚拟化概念

    一.云概念 二.虚拟化,云计算不等于虚拟化,但是云计算需要用到虚拟化的技术 1.服务器虚拟化 2.桌面虚拟化 3.应用虚拟化 三.区别 1.虚拟化是具体的技术. 2.云计算是交付使用的一种模式

  8. Spring Data Redis实现消息队列——发布/订阅模式

    一般来说,消息队列有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式.利用redis这两种场景的消息队列都能够实现. 定义:生产者消费者模式:生产者生产消息放到队列里,多个消费者同时监听队列, ...

  9. 给linux服务器添加一块新的磁盘

    http://www.linuxidc.com/Linux/2011-02/31868.htm 把硬盘装好后,我们用 fdisk -l 查看下: 图中可以看出 /dev/sdb 是500G,新加的硬盘 ...

  10. Python图表数据可视化Seaborn:3. 线性关系数据| 时间线图表| 热图

    1. 线性关系数据可视化 lmplot( ) import numpy as np import pandas as pd import matplotlib.pyplot as plt import ...