多线程使得程序中的多个任务可以同时执行

在一个程序中允许同时运行多个任务。在许多程序设计语言中,多线程都是通过调用依赖系统的过程或函数来实现的

为什么需要多线程?多个线程如何在单处理器系统中同时运行?

多线程可以使您的程序更具响应性和交互性,并提高性能。在许多情况下需要多线程,例如动画和客户端/服务器计算。因为大多数时候CPU处于空闲状态 - 例如,CPU在用户输入数据时什么都不做 - 多个线程在单处理器系统中共享CPU时间是切实可行的。

什么是可运行的对象?什么是线程?
Runnable的一个实例是一个可运行的对象。线程是用于执行可运行任务的可运行对象的包装对象。

1、创建任务和线程

一个任务类必须实现Runnable接口。任务必须从线程运行。

一旦定义了一个TaskClass,就可以用它的构造方法创建一个任务。

例子:

TaskClass task = new TaskClass();

任务必须在线程中执行。使用下面的语句创建任务的线程

Thread thread = new Thread(task);

然后调用start()方法告诉Java虚拟机该线程准备运行

thread.start()

例子:

public class TaskThreadDemo {    

    public static void main(String [] args) {

        Runnable printA = new PrintChar('a', 100);
Runnable printB = new PrintChar('b', 100);
Runnable printNum = new PrintNum(100); Thread thread1 = new Thread(printA);
Thread thread2 = new Thread(printB);
Thread thread3 = new Thread(printNum); thread1.start();
thread2.start();
thread3.start();
}
} class PrintChar implements Runnable{
private char charToPrint;
private int times;
public PrintChar(char c,int t) {
charToPrint = c;
times = t;
} @Override
public void run() {
for(int i=0; i<times; i++) {
System.out.print(charToPrint);
}
}
} class PrintNum implements Runnable{
private int lastNumber;
public PrintNum(int n) {
lastNumber = n;
}
@Override
public void run() {
for(int i=1; i<=lastNumber; i++) {
System.out.print(" " + i);
}
}
}

2、Thread类

3、线程池

之前运用实现Runnable接口来定义一个任务类,以及如何创建一个线程来运行一个任务

该方法对大量的任务而言是不够高效的,为每个任务开始一个新线程可能会限制吞吐量并且造成性能降低。

线程池是管理并发执行任务个数的理想方法。

Java提供Executor接口来执行线程池中的任务,提供ExecutorService接口来管理和控制任务。

Executorservice是Executor的子接口

为了创建一个Executor对象,可以使用Executor类中的静态方法

例子:

public class TaskThreadDemo {    

    public static void main(String [] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
// ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(new PrintChar('a', 100));
executor.execute(new PrintChar('b', 100));
executor.execute(new PrintNum(100)); executor.shutdown(); } }

4、线程同步

例子:

public class AccountWithoutSync {

    private static Account account = new Account();

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i = 0; i < 100; i++) {
executorService.execute(new AddAPennyTask());
} executorService.shutdown(); //等待 全部任务 完成
while(!executorService.isTerminated()) { } System.out.println(account.getBalance()); } private static class AddAPennyTask implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
account.deposit(1);
}
} public static class Account {
private int balance = 0; public int getBalance() {
return balance;
} public void deposit(int amount) {
int newBalance = balance + amount;
try {
Thread.sleep(5);
}catch (InterruptedException e) {
// TODO: handle exception
}
balance = balance + newBalance;
}
} }

这个例子中出错了

原因是:

因为线程不安全,所以数据遭到破坏

synchronized关键字

为避免竞争状态,应该防止多个线程同时进入程序的某一特定部分,程序中的这部分称为临界区。

使用关键字synchronized来同步方法,以便一次只有一个线程可以访问这个方法

例子:

调用一个对象上的同步实例方法,需要给该对象加锁。而调用一个类上的同步静态方法,需要给该类加锁。

同步语句

当执行方法中某一个代码块时,同步语句不仅可用与this对象加锁,而且可用于对任何对象加锁。这个代码块称为同步块。同步语句的一般形式如下:

利用加锁同步

之前的用的同步实例方法

实际上 在执行之前都隐式地需要一个加在实例上的锁

例子:

部分代码:

public static class Account {
private int balance = 0; private Lock lock = new ReentrantLock(); //创建一个锁 public int getBalance() {
return balance;
} public void deposit(int amount) {
lock.lock();//获取该锁
try {
int newBalance = balance + amount; Thread.sleep(5);
balance = newBalance;
}catch (InterruptedException e) {
// TODO: handle exception
}finally {
lock.unlock();//释放该锁
} }
}

线程间协作

例子:

public class ThreadCooperation {
private static Account account = new Account(); public static void main(String [] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(new DepositTask());
executorService.execute(new withdrawTask());
executorService.shutdown(); System.out.println("Thread 1 \t Thread 2 \t Balance");
System.out.println();
} public static class DepositTask implements Runnable{
@Override
public void run() {
try {
while(true) {
account.deposit((int)(Math.random() * 10) + 1);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static class withdrawTask implements Runnable{
@Override
public void run() {
while(true) {
account.withdraw((int)(Math.random() * 10) + 1);
}
}
} public static class Account { private static Lock lock = new ReentrantLock(); private static Condition newDeposit = lock.newCondition(); //线程间的协作 private int balance = 0; public int getBalance() {
return balance;
} public void withdraw(int accountNumber) {
lock.lock();
try {
while(balance < accountNumber) {
System.out.println("\t\tWait for a deposit, wait to withdraw :" + accountNumber);
newDeposit.await();
}
balance -= accountNumber;
System.out.println("\t\tWithdraw: " + accountNumber + "\t\t" + "balance: " + getBalance());
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
lock.unlock();
}
} public void deposit(int accountNumber) {
lock.lock();
try {
balance += accountNumber;
System.out.println("Deposit " + accountNumber + "\t\t\t\t balance: " + getBalance());
newDeposit.signalAll();
}
finally {
lock.unlock();
} }
} }

如何在锁上创建条件?什么是await(),signal()和signalAll()方法?
可以使用lock.newCondition()创建锁上的条件。await()方法使当前线程等待,直到发出条件信号。signal()方法唤醒一个等待线程,signalAll()方法唤醒所有等待线程。

消费者/生产者

public class ComsumerProducer {

    private static Buffer buffer = new Buffer();

    public static void main(String [] args) {

        ExecutorService excurtor = Executors.newFixedThreadPool(2);
excurtor.execute(new ProducerTask());
excurtor.execute(new ConsumerTask()); excurtor.shutdown(); } private static class ProducerTask implements Runnable{ @Override
public void run() {
// TODO Auto-generated method stub
try {
int i = 1;
while(true) {
System.out.println("Producer writes " + i);
buffer.write(i++);
Thread.sleep((int)(Math.random() * 1000));
}
}catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
} private static class ConsumerTask implements Runnable{ @Override
public void run() {
// TODO Auto-generated method stub
try {
while(true) {
System.out.println("\t\tConsumer reads " + buffer.read());
Thread.sleep((int)(Math.random() * 1000));
}
}catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
} } } private static class Buffer{
private static final int CAPACITY = 3; private LinkedList<Integer> queue = new LinkedList<>(); private static Lock lock = new ReentrantLock(); private static Condition notFull = lock.newCondition(); private static Condition notEmpty = lock.newCondition(); public void write(int value) {
lock.lock(); try{
while(queue.size() == CAPACITY) {
System.out.println("Wait for notNull condition");
notFull.await();
}
queue.offer(value);
notEmpty.signal();
} catch (InterruptedException e) { e.printStackTrace();
}finally {
lock.unlock();
} } public int read() {
lock.lock();
int value = 0;
try {
while(queue.isEmpty()) {
System.out.println("\t\tWait for notEmpty condition");
notEmpty.await();
}
value = queue.pop();
notFull.signal();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
lock.unlock();
return value;
} }
} }

阻塞队列

阻塞队列在试图向一个满队列添加元素或者从空队列中删除元素时会导致线程阻塞。BlockingQueue接口继承了Queue,并且提供同步的put和take方法向队列尾部添加元素,以及从队列头部删除元素

java支持三个具体的阻塞队列ArrayBlockingQueue、LinkedBlockingQueue和PriorityBlockingQueue

例子:

用阻塞队列做的消费者/生产者

public class ConsumerProducerUsingBlockingQueue {

    private static ArrayBlockingQueue<Integer> buffer = new ArrayBlockingQueue<Integer>(2);

    public static void main(String [] args) {
ExecutorService excutor = Executors.newFixedThreadPool(2);
excutor.execute(new ProducerTask());
excutor.execute(new ConsumerTask());
excutor.shutdown(); } private static class ProducerTask implements Runnable{ @Override
public void run() {
// TODO Auto-generated method stub
try {
int i = 1;
while(true) {
System.out.println("Producer writes " + i);
buffer.put(i++);
Thread.sleep((int)(Math.random() * 1000));
}
}catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
} private static class ConsumerTask implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
try {
while(true) {
System.out.println("\t\tConsumer reads " + buffer.take());
Thread.sleep((int)(Math.random() * 1000));
}
}catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
} }
}

信号量

例子:

 锁和信号量有什么相似之处和不同之处?
锁和信号量都可用于限制对共享资源的访问。在资源上使用锁可确保只有一个线程可以访问它。在资源上使用信号量允许一个或多个指定数量的线程访问资源。
 如何创建允许三个并发线程的信号量?你如何获得信号量?你如何发布信号量?
使用新的信号量(numberOfPermits)来创建信号量。调用aquire()获取信号量并调用release()来释放信号量。
 
 
避免死锁

 

 什么是死锁?你怎么能避免死锁?
在两个或多个线程获取多个对象上的锁并且每个对象都锁定一个对象并且正在等待另一个对象上的锁定的情况下发生死锁。资源排序技术可用于避免死锁。

同步合集

并行编程

java 多线程和并行程序设计的更多相关文章

  1. [Java多线程]-并发,并行,synchonrized同步的用法

    一.多线程的并发与并行: 并发:多个线程同时都处在运行中的状态.线程之间相互干扰,存在竞争,(CPU,缓冲区),每个线程轮流使用CPU,当一个线程占有CPU时,其他线程处于挂起状态,各线程断续推进. ...

  2. JAVA多线程---高并发程序设计

    先行发生原则 程序顺序原则:一个线程内保证语义的串行性 volatile:volatile变量的写,先发生于读,这保证了volatile变量的可见性 锁规则:解锁必然发生在随后的加锁前 传递性:A优先 ...

  3. Java多线程程序设计详细解析

    一.理解多线程 多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立. 线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线 ...

  4. Java多线程--并行模式与算法

    Java多线程--并行模式与算法 单例模式 虽然单例模式和并行没有直接关系,但是我们经常会在多线程中使用到单例.单例的好处有: 对于频繁使用的对象可以省去new操作花费的时间: new操作的减少,随之 ...

  5. 已看1.熟练的使用Java语言进行面向对象程序设计,有良好的编程习惯,熟悉常用的Java API,包括集合框架、多线程(并发编程)、I/O(NIO)、Socket、JDBC、XML、反射等。[泛型]\

    1.熟练的使用Java语言进行面向对象程序设计,有良好的编程习惯,熟悉常用的Java API,包括集合框架.多线程(并发编程).I/O(NIO).Socket.JDBC.XML.反射等.[泛型]\1* ...

  6. Java多线程原理+基础知识(超级超级详细)+(并发与并行)+(进程与线程)1

    Java多线程 我们先来了解两个概念!!!! 1.什么是并发与并行 2.什么是进程与线程 1.什么是并发与并行 1.1并行:两个事情在同一时刻发生 1.2并发:两个事情在同一时间段内发生 并发与并行的 ...

  7. Java多线程专题1: 并发与并行的基础概念

    合集目录 Java多线程专题1: 并发与并行的基础概念 什么是多线程并发和并行? 并发: Concurrency 特指单核可以处理多任务, 这种机制主要实现于操作系统层面, 用于充分利用单CPU的性能 ...

  8. 从JAVA多线程理解到集群分布式和网络设计的浅析

    对于JAVA多线程的应用非常广泛,现在的系统没有多线程几乎什么也做不了,很多时候我们在何种场合如何应用多线程成为一种首先需要选择的问题,另外关于java多线程的知识也是非常的多,本文中先介绍和说明一些 ...

  9. 学习笔记之JAVA多线程

    Java程序设计实用教程 by 朱战立 & 沈伟 孙鑫Java无难事 Java 多线程与并发编程专题(http://www.ibm.com/developerworks/cn/java/j-c ...

随机推荐

  1. P2157 [SDOI2009]学校食堂

    题目描述 小F 的学校在城市的一个偏僻角落,所有学生都只好在学校吃饭.学校有一个食堂,虽然简陋,但食堂大厨总能做出让同学们满意的菜肴.当然,不同的人口味也不一定相同,但每个人的口味都可以用一个非负整数 ...

  2. day10 前向引用

    风湿理论,函数即变量.没定义没加载(开辟内存空间).都是没法用的 不定义调用必然出错 def foo(): print("from foo") bar () # 标红 foo() ...

  3. Spring Security和 JWT两大利器来打造一个简易的权限系统。

    写在前面 关于 Spring Security Web系统的认证和权限模块也算是一个系统的基础设施了,几乎任何的互联网服务都会涉及到这方面的要求.在Java EE领域,成熟的安全框架解决方案一般有 A ...

  4. [luogu5002]专心OI - 找祖先

    [传送门] 我们还是先将一下算法的步骤,待会再解释起来方便一点. 算法步骤 首先我们算出每个子树的\(size\). 我们就设当前访问的节点 然后我们就得到了当前这个节点的答案是这个树整个的\(siz ...

  5. emWin 之 WM_SetCallback 创建回调函数

    @2018-7-11 [小记] emWin 通过函数 WM_SetCallback ( )  自定义回调函数 > 下段代码就是通过 MESSAGEBOX 控件的 OK 按键实现删除信息框的对话框 ...

  6. elasticsearch 安装、配置

    elasticsearch:基于java开发,基于RESTful web 接口,提供分布式多用户能力的全文搜索引擎. elasticsearch 安装: 1. java SE Development ...

  7. 使用fiddler模拟http请求

    概述  与httpwath相比,fiddler能模拟http请求.能断点调试.http分析统计吸引了我,使用之后感觉这个工具非常不错,这篇文章只单介绍一下fiddler工作原理,简单介绍一下它的重要功 ...

  8. 【洛谷P1717】钓鱼

    题目大意:给定 N 个位置,每个位置有一个答案贡献值,在一个位置加了一次该位置的答案贡献值之后,该值会减掉一部分,从一个位置移动到另一个位置需要花费一定的时间,问:给定 M 单位的时间,如何移动使得答 ...

  9. 【POJ1961】最短周期串/最大周期 kmp

    引理:\(s[1,i]\) 具有长度为 \(len\) 的循环节的充要条件是:\(len\ |\ i,s[1,i-len]=s[len+1,i]\). 代码如下 #include <cstdio ...

  10. Asp: Server.mapPath() 注意事项

    今天下午,在删除一个用户后,竟然发现该用户上传的图片还能正常显示. 郁闷了,怎么会这样? 为了再次验证这个结果,我新注册一个用户名,然后上传3张图片,接着删除这个用户,再查看刚刚上传的3张图片,竟然没 ...