线程有有序性和可见性

多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现。

在多个线程之间共享类的一个对象,这个对象是被创建在主内存(堆内存)中,每个线程都有自己的工作内存(线程栈),工作内存存储了主内存对象的一个副本,当线程操作对象时,首先从主内存复制对象到工作内存中,然后执行代码改变了值,最后用工作内存刷新主内存。

当一个对象在多个内存中都存在副本时,如果一个内存修改了共享变量,其它线程也应该能够看到被修改后的值,此为可见性

多个线程执行时,CPU对线程的调度是随机的,我们不知道当前程序被执行到哪步就切换到了下一个线程。为保证线程有序执行此为有序性

使用synchronized修饰的方法或者代码块可以看成是一个原子操作

每个锁对象(JLS中叫monitor)都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个线程被唤醒(notify)后,才会进入到就绪队列,等待CPU的调度,反之,当一个线程被wait后,就会进入阻塞队列,等待下一次被唤醒。看我们的例子,当第一个线程执行输出方法时,获得同步锁,执行输出方法,恰好此时第二个线程也要执行输出方法,但发现同步锁没有被释放,第二个线程就会进入就绪队列,等待锁被释放。一个线程执行互斥代码过程如下:

1. 获得同步锁;

2. 清空工作内存;

3. 从主内存拷贝对象副本到工作内存;

4. 执行代码(计算或者输出等);

5. 刷新主内存数据;

6. 释放同步锁。

所以,synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性。

volatile是第二种Java多线程同步的机制,一个变量可以被volatile修饰,在这种情况下内存模型(主内存和线程工作内存)确保所有线程可以看到一致的变量值。

JAVA线程创建方式

1、继承Thread类创建线程类

继承Thread类并重写该类的run方法,该run方法代表了线程要完成的任务。

2、通过Runnable接口创建线程类

实现runnable接口,重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。将Runnable实现类实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

3、通过Callable和Future创建线程

(1)实现Callable接口,重写call()方法,该call()方法将作为线程执行体,并且有返回值。

(2)使用FutureTask类来包装Callable对象。

(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。

(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

Callable和Future

Callable接口类似于Runnable,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable被线程执行后,可以返回值,这个返回值可以被Future拿到,下面来看一个简单的例子:

public class CallableAndFuture {

public static void main(String[] args) {

Callable<Integer> callable = new Callable<Integer>() {

public Integer call() throws Exception {

return new Random().nextInt(100);

}

};

FutureTask<Integer> future = new FutureTask<Integer>(callable);

new Thread(future).start();

try {

Thread.sleep(5000);// 可能做一些事情

System.out.println(future.get());

} catch (InterruptedException e) {

e.printStackTrace();

} catch (ExecutionException e) {

e.printStackTrace();

}

}

}

FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值,那么这个组合的使用有什么好处呢?假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到,岂不美哉!
       下面来看另一种方式使用Callable和Future,通过ExecutorService的submit方法执行Callable,并返回Future,代码如下:

public class CallableAndFuture {

public static void main(String[] args) {

ExecutorService threadPool = Executors.newSingleThreadExecutor();

Future<Integer> future = threadPool.submit(new Callable<Integer>() {

public Integer call() throws Exception {

return new Random().nextInt(100);

}

});

try {

Thread.sleep(5000);// 可能做一些事情

System.out.println(future.get());

} catch (InterruptedException e) {

e.printStackTrace();

} catch (ExecutionException e) {

e.printStackTrace();

}

}

}

执行多个带返回值的任务,并取得多个返回值,代码如下:

public class CallableAndFuture {

public static void main(String[] args) {

ExecutorService threadPool = Executors.newCachedThreadPool();

CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);

for(int i = 1; i < 5; i++) {

final int taskID = i;

cs.submit(new Callable<Integer>() {

public Integer call() throws Exception {

return taskID;

}

});

}

// 可能做一些事情

for(int i = 1; i < 5; i++) {

try {

System.out.println(cs.take().get());

} catch (InterruptedException e) {

e.printStackTrace();

} catch (ExecutionException e) {

e.printStackTrace();

}

}

}

}

  其实也可以不使用CompletionService,可以先创建一个装Future类型的集合,用Executor提交的任务返回值添加到集合中,最后遍历集合取出数据,代码略。提交到CompletionService中的Future是按照完成的顺序排列的,这种做法中Future是按照添加的顺序排列的。

 

线程协作-生产者/消费者问题

处理线程协作时,在同步方法中,必须作进入检查,如果不满足进入条件,须放弃锁,进入等待

完成业务处理后,要做清场处理,同时完成唤醒其他等待的线程的操作

1. import java.util.ArrayList;  
2. import java.util.List;  
3. /** 定义一个盘子类,可以放鸡蛋和取鸡蛋 */  
4. publicclass Plate {  
5.     /** 装鸡蛋的盘子 */  
6.     List<Object> eggs = new ArrayList<Object>();  
7.     /** 取鸡蛋 */  
8.     publicsynchronized Object getEgg() {  
9.         while (eggs.size() == 0) {  
10.             try {  
11.                 wait();  
12.             } catch (InterruptedException e) {  
13.                 e.printStackTrace();  
14.             }  
15.         }  
16.         Object egg = eggs.get(0);  
17.         eggs.clear();// 清空盘子  
18.         notify();// 唤醒阻塞队列的某线程到就绪队列  
19.         System.out.println("拿到鸡蛋");  
20.         return egg;  
21.     }  
22.     /** 放鸡蛋 */  
23.     publicsynchronizedvoid putEgg(Object egg) {  
24.         while (eggs.size() > 0) {  
25.             try {  
26.                 wait();  
27.             } catch (InterruptedException e) {  
28.                 e.printStackTrace();  
29.             }  
30.         }  
31.         eggs.add(egg);// 往盘子里放鸡蛋  
32.         notify();// 唤醒阻塞队列的某线程到就绪队列  
33.         System.out.println("放入鸡蛋");  
34.     }  
35.     staticclass AddThread implements Runnable  {  
36.         private Plate plate;  
37.         private Object egg = new Object();  
38.         public AddThread(Plate plate) {  
39.             this.plate = plate;  
40.         }  
41.         publicvoid run() {  
42.             plate.putEgg(egg);  
43.         }  
44.     }  
45.     staticclass GetThread implements Runnable  {  
46.         private Plate plate;  
47.         public GetThread(Plate plate) {  
48.             this.plate = plate;  
49.         }  
50.         publicvoid run() {  
51.             plate.getEgg();  
52.         }  
53.     }  
54.     publicstaticvoid main(String args[]) {  
55.         Plate plate = new Plate();  
56.         for(int i = 0; i < 10; i++) {  
57.             new Thread(new AddThread(plate)).start();  
58.             new Thread(new GetThread(plate)).start();  
59.         }  
60.     }  
61. }  

JAVA线程池

1.  创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程。

ExecutorService threadPool = Executors.newFixedThreadPool(3);// 创建可以容纳3个线程的线程池

2. 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。

ExecutorService threadPool = Executors.newCachedThreadPool();// 线程池的大小会根据执行的任务数动态分配

3. 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。

ExecutorService threadPool = Executors.newSingleThreadExecutor();// 创建单个线程的线程池,如果当前线程在执行任务时突然中断,则会创建一个新的线程替代它继续执行任务

4. 创建一个可安排在给定延迟后运行命令或者定期地执行的线程池。

ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);// 效果类似于Timer定时器

JAVA线程池其实也是一个生产者和消费者模式

线程池模拟:

package threadpool;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit; public class ThreadPool
{
private int threadNum; private LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();; private TaskThread[] taskThread; public ThreadPool(int threadNum)
{
this.threadNum = threadNum; taskThread = new TaskThread[threadNum]; for (int i=0; i<threadNum; i++)
{
taskThread[i] = new TaskThread(i);
taskThread[i].start();
}
} public void execute(Runnable run)
{
queue.offer(run);
} public void destroy()
{
while (!queue.isEmpty())
{
System.out.println(queue.size() + " job(s) not finish!");
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
} System.out.println("begin to destroy"); for (int i=0; i<threadNum; i++)
{
taskThread[i].setDone();
} for (int i=0; i<threadNum; i++)
{
try
{
taskThread[i].join();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
} System.out.println(queue.size() + " job(s) not finish!"); queue.clear();
} private final class TaskThread extends Thread
{
private boolean done = false; private int no; public TaskThread(int no)
{
super();
this.no = no;
} public void run()
{
Runnable run = null;
while (!done)
{
System.out.println("task " + no + " ready...");
try
{
run = queue.poll(1, TimeUnit.SECONDS);
if (null == run)
{
continue;
}
System.out.println("task " + no + " get job");
Thread.sleep(500);
run.run();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
System.out.println("task " + no + " done");
} public void setDone()
{
done = true;
}
} public static void main(String[] args)
{
ThreadPool pool = new ThreadPool(5); pool.execute(new Runnable(){
public void run()
{
System.out.println("Runnable 1 added");
}
});
pool.execute(new Runnable(){
public void run()
{
System.out.println("Runnable 2 added");
}
});
pool.execute(new Runnable(){
public void run()
{
System.out.println("Runnable 3 added");
}
});
pool.execute(new Runnable(){
public void run()
{
System.out.println("Runnable 4 added");
}
}); pool.destroy();
} }

Java多线程学习开发笔记的更多相关文章

  1. java多线程学习笔记——详细

    一.线程类  1.新建状态(New):新创建了一个线程对象.        2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...

  2. JAVA多线程学习笔记(1)

    JAVA多线程学习笔记(1) 由于笔者使用markdown格式书写,后续copy到blog可能存在格式不美观的问题,本文的.mk文件已经上传到个人的github,会进行同步更新.github传送门 一 ...

  3. Java多线程学习笔记(一)——多线程实现和安全问题

    1. 线程.进程.多线程: 进程是正在执行的程序,线程是进程中的代码执行,多线程就是在一个进程中有多个线程同时执行不同的任务,就像QQ,既可以开视频,又可以同时打字聊天. 2.线程的特点: 1.运行任 ...

  4. Java多线程学习笔记

    进程:正在执行中的程序,其实是应用程序在内存中运行的那片空间.(只负责空间分配) 线程:进程中的一个执行单元,负责进程汇总的程序的运行,一个进程当中至少要有一个线程. 多线程:一个进程中时可以有多个线 ...

  5. Java多线程学习(转载)

    Java多线程学习(转载) 时间:2015-03-14 13:53:14      阅读:137413      评论:4      收藏:3      [点我收藏+] 转载 :http://blog ...

  6. 【转】Java多线程学习

    来源:http://www.cnblogs.com/samzeng/p/3546084.html Java多线程学习总结--线程概述及创建线程的方式(1) 在Java开发中,多线程是很常用的,用得好的 ...

  7. Java多线程学习(三)volatile关键字

    转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79680693 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...

  8. Java多线程学习(一)Java多线程入门

    转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79640870 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...

  9. 转:Java多线程学习(总结很详细!!!)

    Java多线程学习(总结很详细!!!) 此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更上一个台阶呢? 本文主要讲java中多线程 ...

随机推荐

  1. 《精通c#(第6版)》【PDF】下载

    图灵程序设计丛书:精通C#(第6版)是C#领域久负盛名的经典著作,深入全面地讲解了C#编程语言和.NET平台的核心内容,并结合大量示例剖析相关概念.全书分为八部分:C#和.NET平台.C#核心编程结构 ...

  2. Android(Java) 字符串的常用操作,获取指定字符出现的次数,根据指定字符截取字符串

    /*这是第100000份数据,要截取出100000*/ String s="这是第100000份数据"; String s1 = s.substring(s.indexOf(&qu ...

  3. Vue2 后台管理系统解决方案

    基于Vue.js 2.x系列 + Element UI 的后台管理系统解决方案. github地址:https://github.com/lin-xin/manage-system demo地址:ht ...

  4. Hosts文件实际应用 配置内部服务器提高访问效率和速度

    一 hosts文件的作用和介绍 https://jingyan.baidu.com/article/335530da45485e19cb41c3d6.html https://www.cnblogs. ...

  5. ADODB.Connection、ADODB.RecordSet

    1.数据库连接对象(ADODB. Connection)该对象用于与ODBC数据库建立连接,所有对数据库的操作均通过该连接进行.数据库连接对象ADODB. Connection的作用象Delphi中的 ...

  6. 原创js自动补全---auotocomplete

    if ($("input.autocomplete_input").length > 0) { $("input.autocomplete_input") ...

  7. window下nginx的常用命令

    window nginx 启动 常用命令 2016-05-04 11:11 214人阅读 评论(0) 收藏 举报 分类: nginx(5) 版权声明:本文为博主原创文章,未经博主允许不得转载. 启动 ...

  8. eKing Cloud基础云平台演进之路

    出口转内销.首发于公司微信公众号,作者本人,现转载到此.本来写得比较技术,还算有点干货,但是结果被编辑咔咔咔,就只剩下下面这些内容. 大型企业如何开启自己的快速上云之路? 2017-12-08 易建科 ...

  9. Linq To EF

    l简单查询:var result = from c in Entities.Customer select c; l条件查询: 普通linq写法: var result = from c in Ent ...

  10. css div 细边框

    .item{ max-width:48%; float:left; padding:2px; border-top:1px solid #000; border-left:1px solid #000 ...