多线程

1.1      多线程介绍

进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。

1.2      Thread类

通过API中搜索,查到Thread类。通过阅读Thread类中的描述。Thread是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。

l  构造方法

l  常用方法

发现创建新执行线程有两种方法。

一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。创建对象,开启线程。run方法相当于其他线程的main方法。

另一种方法是声明一个实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。

创建线程的步骤:

1 定义一个类继承Thread。

2 重写run方法。

3 创建子类对象,就是创建线程对象。

4 调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。

package com.oracle.xiancheng;

public class Demo01 extends Thread {

public static void main(String[] args) {

//创建线程

MyThread mt=new MyThread();

//创建线程

mt.start();

//获取正在执行的对象名称  调用 getname

for(int i=0;i<100;++i){

System.out.println("main-------"+i);

}

}

}

自定义线程类

package com.oracle.Runnable;

public class MyRunnable implements Runnable{

@Override

public void run() {

for(int i=0;i<50;i++){

System.out.println("run-----"+i);

}

}

}

1.2.1    实现Runnable的原理

实现Runnable接口,避免了继承Thread类的单继承局限性。覆盖Runnable接口中的run方法,将线程任务代码定义到run方法中。

创建Thread类的对象,只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务。

1.2.2    实现Runnable的好处

第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。

1.3      线程的匿名内部类使用

package com.oracle.Runnable;

public class MyRunnable implements Runnable{

@Override

public void run() {

for(int i=0;i<50;i++){

System.out.println("run-----"+i);

}

}

}

package com.oracle.Runnable;

public class Demo02 {

public static void main(String[] args) {

//创建线程子类对象

//匿名内部类对象

//创建线程对象时,直接重写Thread类中的run方法

Thread th=new Thread(){

public void run() {

System.out.println(Thread.currentThread().getName()+"run");

};

};

//开启线程

th.start();

//使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法

/*Runnable r=new Runnable(){

public void run() {

System.out.println(Thread.currentThread().getName()+"run");

};

};

//创建线程任务对象

Thread th=new Thread(r);

//开启线程

th.start();*/

}

}

运行结果:

线程池

2.1      线程池概念

线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源

2.2      使用线程池方式--Runnable接口

l  Executors:线程池创建工厂类

public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象

l  ExecutorService:线程池类

Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行

l  Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用

l  使用线程池中线程对象的步骤:

创建线程池对象

创建Runnable接口子类对象

提交Runnable接口子类对象

关闭线程池

package com.oracle.Runnable;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class Demo03 {

public static void main(String[] args) {

// Executors 线程池工厂类

// ExecutorService 线程池工厂类

// 获取线程池对象

ExecutorService es = Executors.newFixedThreadPool(2);

// 创建线程任务对象

MyRunnable mr = new MyRunnable();

// 执行线程任务

es.submit(mr);

es.submit(mr);

es.submit(mr);

//释放资源

es.shutdown();

}

}

package com.oracle.Runnable;

public class MyRunnable implements Runnable{

@Override

public void run() {

for(int i=0;i<50;i++){

System.out.println("run-----"+i);

}

}

}

运行结果:

等等

2.3      使用线程池方式—Callable接口

Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。

ExecutorService:线程池类

<T> Future<T> submit(Callable<T> task):获取线程池中的某一个线程对象,并执行线程中的call()方法

Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用

使用线程池中线程对象的步骤:

创建线程池对象

创建Callable接口子类对象

提交Callable接口子类对象

关闭线程池

package com.oracle.Runnable;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {

@Override

public String call() throws Exception {

return "abc";

}

}

package com.oracle.Runnable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorCompletionService;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

public class Demo04 {

public static void main(String[] args) throws InterruptedException, ExecutionException {

//创建线程任务

MyCallable mc=new MyCallable();

//获取线程池工厂

ExecutorService es=Executors.newFixedThreadPool(2);

Future<String> f=es.submit(mc);

//创建返回值

String  str=f.get();

System.out.println(str);

}

}

运行结果:

2.4      线程池练习:返回两个数相加的结果和乘积的结果

package com.oracle.Demo01;

import java.util.concurrent.Callable;

public class MyCallables implements Callable<Integer> {

private int num1;

private int num2;

public MyCallables(int num1,int num2) {

this.num1=num1;

this.num2=num2;

}

@Override

public Integer call() throws Exception {

return num1+num2;

}

}

package com.oracle.Demo01;

import java.math.BigInteger;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

public class Test1 {

public static void main(String[] args) throws InterruptedException, ExecutionException {

//和

MyCallables mc1=new MyCallables(100,150);

MyCallables mc2=new MyCallables(10,15);

ExecutorService es=Executors.newFixedThreadPool(2);

Future<Integer> num1=es.submit(mc1);

Future<Integer> num2=es.submit(mc2);

int s1=num1.get();

int  s2=num2.get();

System.out.println(s1);

System.out.println(s2);

es.shutdown();

}

}

运行结果:

积:

package com.oracle.Demo01;

import java.math.BigDecimal;

import java.math.BigInteger;

import java.util.concurrent.Callable;

public class MyCallablesr implements Callable<String > {

private int num;

public MyCallablesr(int num) {

this.num=num;

}

@Override

public String call() throws Exception {

String  base="1";//超long的范围

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

//用BigDecimal转换

BigDecimal stra=new BigDecimal(base);

BigDecimal end=new BigDecimal(i);

BigDecimal re=end.multiply(stra);

base=re.toString();

}

return base ;

}

}

package com.oracle.Demo01;

import java.math.BigInteger;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

public class Test1 {

public static void main(String[] args) throws InterruptedException, ExecutionException {

//积

MyCallablesr ms1=new  MyCallablesr(100);

MyCallablesr ms2=new MyCallablesr(200);

ExecutorService es=Executors.newFixedThreadPool(2);

Future<String> base1=es.submit(ms1);

Future<String> base2=es.submit(ms2);

String s1=base1.get();

String s2=base2.get();

System.out.println(s1);

System.out.println(s2);

es.shutdown();

}

}

运行结果:

多线程

3.1      线程安全

如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

l  我们通过一个案例,演示线程的安全问题:

电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “功夫熊猫3”,本次电影的座位共100个(本场电影只能卖100张票)。

我们来模拟电影院的售票窗口,实现多个窗口同时卖 “功夫熊猫3”这场电影票(多个窗口一起卖这100张票)

需要窗口,采用线程对象来模拟;需要票,Runnable接口子类来模拟

3.2      线程同步(线程安全处理Synchronized)

java中提供了线程同步机制,它能够解决上述的线程安全问题。

线程同步的方式有两种:

方式1:同步代码块

方式2:同步方法

3.2.1    同步代码块

同步代码块: 在代码块声明上 加上synchronized

synchronized (锁对象) {

可能会产生线程安全问题的代码

}

同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。

模拟售票:

package com.oracle.xianchengchi;

public class MyRunnable implements Runnable {

// 卖电影票

private int ticket = 100;

private Object obj = new Object();

@Override

public void run() {

while (true) {

synchronized (obj) {

if (ticket > 0) {

try {

Thread.sleep(50);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");

}

}

}

}

}

测试:

package com.oracle.xianchengchi;

public class Test01 {

public static void main(String[] args) {

//明确线程任务

MyRunnable mr=new MyRunnable();

Thread t0=new Thread(mr);

Thread t1=new Thread(mr);

Thread t2=new Thread(mr);

//开启线程

t0.start();

t1.start();

t2.start();

}

}

运行结果:

3.2.2    同步方法

l  同步方法:在方法声明上加上synchronized

public synchronized void method(){

可能会产生线程安全问题的代码

}

         同步方法中的锁对象是 this

使用同步方法,对电影院卖票案例中Ticket类进行如下代码修改:

package com.oracle.xianchengchi;

public class MyRunnables implements Runnable {

// 卖电影票

private int ticket = 100;

private Object obj = new Object();

@Override

public void run() {

while (true) {

sale();

}

}

public synchronized void sale() {

if (ticket > 0) {

try {

Thread.sleep(50);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");

}

}

}

package com.oracle.xianchengchi;

public class Test02 {

public static void main(String[] args) {

//明确线程任务

MyRunnables mr=new MyRunnables();

Thread t0=new Thread(mr);

Thread t1=new Thread(mr);

Thread t2=new Thread(mr);

//开启线程

t0.start();

t1.start();

t2.start();

}

}

运行结果:

3.3      Lock接口

查阅API,查阅Lock接口描述,Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。

l  Lock接口中的常用方法

Lock提供了一个更加面对对象的锁,在该锁中提供了更多的操作锁的功能。

我们使用Lock接口,以及其中的lock()方法和unlock()方法替代同步,对电影院卖票案例中Ticket类进行如下代码修改:

package com.oracle.xianchengchi;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

public class MyRunnable2 implements Runnable {

// 卖电影票

private int ticket = 100;

private Lock lock=new ReentrantLock();

@Override

public void run() {

while (true) {

lock.lock();

if (ticket > 0) {

try {

Thread.sleep(50);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");

}

lock.unlock();

}

}

Java多线程、线程池和线程安全整理的更多相关文章

  1. java多线程详解(7)-线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, 这样频繁创建线程就会大大降低系 ...

  2. java多线程总结五:线程池的原理及实现

    1.线程池简介:     多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.        假设一个服务器完成一项任务所需时间为:T1 创 ...

  3. java多线程(四)-自定义线程池

    当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的 ...

  4. -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

     本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁  sleep()和wait()方法的区别 为什么wait( ...

  5. java多线程系列六、线程池

    一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池. 2. 使用线程池的好处 a) 降低资源的消耗.使用线程池不用频繁的创建线程和销毁线程 b) 提高响应速度,任 ...

  6. Java多线程面试题:线程锁+线程池+线程同步等

    1.并发编程三要素? 1)原子性 原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行. 2)可见性 可见性指多个线程操作一个共享变量时,其中一个线程对变量 ...

  7. (Java多线程系列九)线程池

    线程池 1.什么是线程池 线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程.线程池中线程的数量通常取决于可用内存数量和应用程序的需求. ...

  8. Java多线程并发04——合理使用线程池

    在此之前,我们已经了解了关于线程的基本知识,今天将为各位带来,线程池这一技术.关注我的公众号「Java面典」了解更多 Java 相关知识点. 为什么使用线程池?线程池做的工作主要是控制运行的线程的数量 ...

  9. Java多线程系列 JUC线程池03 线程池原理解析(二)

    转载  http://www.cnblogs.com/skywang12345/p/3509954.html  http://www.cnblogs.com/skywang12345/p/351294 ...

随机推荐

  1. User Agent 大全

    一.基础知识篇: Http Header之User-Agent User Agent中文名为用户代理,是Http协议中的一部分,属于头域的组成部分,User Agent也简称UA.它是一个特殊字符串头 ...

  2. MySQL服务器的安装和配置,MySQL Workbench 8.0.12安装,MySQL的基本使用

    一 MySQL服务器的安装和配置 二 MySQL Workbench 8.0.12安装 三 MySQL的基本使用 一MySQL服务器的安装和配置 MySQL是目前最为流行的开放源码的数据库,是完全网络 ...

  3. LeetCode算法题-Count Binary Substrings(Java实现)

    这是悦乐书的第293次更新,第311篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第161题(顺位题号是696).给定一个字符串s,计算具有相同数字0和1的非空且连续子串 ...

  4. 【模块04-大数据技术入门】02节-HDFS核心知识

    分布式存储 (1) 5PB甚至更大的数据集怎么存储 ? 所有数据分块,每个数据块冗余存储在多台机器上(冗余可提高数据块高可用性).另外一台机器上启动一个管理所有节点.以及存储在各节点上面数据块的服务. ...

  5. 2019年桌面Linux需要做好的7件事

    2019年桌面Linux需要做好的7件事 新的一年已经到来,这意味着又一年过去了,Linux还是没有发现自己主宰了桌面.Linux在许多方面做得非常好,在接下来的几周,我们将研究一些最适合您各种需求的 ...

  6. golang http自动转为https 如何跳过证书检查

    func SendReq(req *http.Request,result interface{}) error { tr := &http.Transport{ TLSClientConfi ...

  7. ubuntu安装docker{ubuntu16.04下安装docker}

       一.开始安装 第一步:   由于apt官方库里的docker版本可能比较旧,所以先卸载可能存在的旧版本: $ sudo apt-get remove docker docker-engine d ...

  8. 虚拟机系统安装Messenger和Server

    YCD对支持虚拟机运行Messenger和Server, 不论用哪种虚拟机安装, 请保证: 确认DirectX和Direct 3D组件已经开启 为虚拟机分配显卡计算资源 如果不满足以上条件, 虚拟机上 ...

  9. 使用jquery中$.each()方法来循环一个数据列表

    定义和用法 jQuery.each() 函数用于遍历指定的对象和数组. 语法 $.each( object, callback ) 参数 描述 object Object类型 指定需要遍历的对象或数组 ...

  10. Python基础知识5-递归函数、生成器

    函数执行流程* 递归Recursion 递归的性能  递归总结 递归练习: def fac(n): if n==1: return n return n*fac(n-1) def fac1(n, f= ...