线程

Java使用 java.lang.Thread 类代表线程,所有的线程对象都必须是Thread类或其子类的实例

Thread类常用方法

构造方法

  • public Thread():分配一个新的线程对象。

  • public Thread(String name):分配一个指定名字的新的线程对象。

  • public Thread(Runnable target):分配一个带有指定目标新的线程对象。

  • public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。

常用方法

  • public string getName():获取当前线程名称。

  • public void start():导致此线程开始执行;Java虚拟机调用此线程的run方法。

  • public void run():此线程要执行的任务在此处定义代码。

  • public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。

  • public static Thread currentThread():返回对当前正在执行的线程对象的引用。

创建线程方式一

Java中通过继承Thread类来创建并启动多线程的步骤如下:

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把 run()方法称为线程执行体。

  2. 创建Thread子类的实例,即创建了线程对象

  3. 调用线程对象的start()方法来启动该线程

测试类:

public class Demo{
public static void main(String[] args) {
// 创建自定义线程对象
MyThread mt = new MyThread("新线程");
// 开启新线程
mt.start();
// 在主方法中执行for循环
for (int i = 0; i < 10; i++) {
System.out.println("main线程正在执行" + i);
}
}
}

自定义线程类:

public class MyThread extends Thread {
// 定义指定线程名称的构造方法
public MyThread(String name) {
// 调用父类的String参数的构造方法,指定线程的名称
super(name);
}
/**
* 重写run方法,完成该线程执行的逻辑
*/
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + "正在执行" + i);
}
}
}

流程图:

程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。

随着调用mt的对象的start方法,另外一个新的线程也启动了,这样,整个应用就在多线程下运行。

创建线程方式二

Java中通过实现Runnable接口来创建并启动多线程的步骤如下:

  1. 定义Runnable接口的实现类,并重写该接口的run () 方法,该run () 方法的方法体同样是该线程的线程执行体。

  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

  3. 调用线程对象的start () 方法来启动线程。

测试类:

public class Demo {
public static void main(String[] args) {
// 创建自定义类对象线程任务对象
MyRunnable mr = new MyRunnable();
// 创建线程对象
Thread t = new Thread(mr, "新线程");
t.start();
for (int i = 0; i < 10; i++) {
System.out.println("main线程正在执行" + i);
}
}
}

自定义线程类:

public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "正在执行" + i);
}
}
}

通过实现Runnable接口,使得该类有了多线程类的特征。run () 方法是多线程程序的一个执行目标。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。

Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

实现Runnable接口比继承Thread类所具有的优势:

  1. 适合多个相同的程序代码的线程去共享同一个资源。

  2. 可以避免java中的单继承的局限性。

  3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和数据独立。

  4. 线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类。

匿名内部类方式实现线程的创建

public class Demo {
public static void main(String[] args) {
//使用匿名内部类方法;直接创建Thread类的子类对象
/*
* new Thread() { public void run() { for (int i = 0; i < 10; i++) {
* System.out.println("新线程正在执行" + i); } } }.start();
*/
//使用匿名内部类方式;直接创建Runnable接口实现类对象
Runnable r = new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("新线程正在执行" + i);
}
}
};
new Thread(r).start();
for (int i = 0; i < 10; i++) {
System.out.println("main线程正在执行" + i);
}
}
}

线程安全

两个或两个以上的线程在访问共享资源时,仍然能得到正确的结果则称之为线程安全

模拟卖50张电影票

public class Ticket implements Runnable {
private int ticket = 50;
// 买票操作
@Override
public void run() {
// 每个窗口买票操作,窗口永远开启
while (true) {
if (ticket > 0) {
// 使用sleep方法模拟买票
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 获取当前对象的名字
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖票:" + ticket--);
}
}
}
}

测试类:

public class Demo {
public static void main(String[] args) {
// 创建线程任务对象
Ticket ticket = new Ticket();
// 创建三个窗口卖票
Thread t1 = new Thread(ticket, "窗口一");
Thread t2 = new Thread(ticket, "窗口二");
Thread t3 = new Thread(ticket, "窗口三");

// 同时开始卖票
t1.start();
t2.start();
t3.start();
}
}

结果出现了这种现象:

这种问题,几个窗口(线程)票数不同步了,称为线程不安全

线程同步

当我们使用多个线程访问统一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题.

要解决上述多线程并发访问多一个资源的安全性问题,java中提供了同步机制(synchronized)来解决,有三种方式完成同步操作:

  1. 同步代码块

  2. 同步方法

  3. 锁机制

同步代码块

同步代码块:synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问

synchronized(同步锁){
需要同步操作的代码
}
同步锁注意事项 

1.锁对象可以是任意类型。 

2.多个线程对象要使用同一把锁。

同步代码块实现线程安全

public class Ticket implements Runnable {
private int ticket = 50;
Object lock = new Object();

// 买票操作
@Override
public void run() {
// 每个窗口买票操作,窗口永远开启
while (true) {
synchronized (lock) {
if (ticket > 0) {
// 使用sleep方法模拟买票
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 获取当前对象的名字
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖票:" + ticket--);
}
}
}
}
}

同步方法

同步方法:使用synchronized修饰的方法,就叫做同步方法保证A线程执行该方法的时候,其他线程只能在方法外等着

public synchronized void method(){
可能会产生线程安全问题的代码
}

同步方法实现线程安全

public class Ticket implements Runnable {
private int ticket = 50;
// 买票操作
@Override
public void run() {
// 每个窗口买票操作,窗口永远开启
while (true) {
sellTicket();
}
}
// 锁对象是谁调用这个方法就是谁,this
public synchronized void sellTicket() {
if (ticket > 0) {
// 使用sleep方法模拟买票
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 获取当前对象的名字
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖票:" + ticket--);
}
}
}

Lock锁

java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。 Lock常用方法

  • public void lock():加同步锁。

  • public void unlock():释放同步锁。

Lock锁实现线程安全

public class Ticket implements Runnable {
private int ticket = 50;
Lock lock = new ReentrantLock();
// 买票操作
@Override
public void run() {
// 每个窗口买票操作,窗口永远开启
while (true) {
lock.lock();
if (ticket > 0) {
// 使用sleep方法模拟买票
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 获取当前对象的名字
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖票:" + ticket--);
}
lock.unlock();
}
}

}

线程池

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

线程池能够带来三个好处:

  1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

  2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。

  3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存

线程池的使用

Java里面线程池的顶级接口是java.util.concurrent.Executors

public static ExecutorService newFixedThreadPool(int nThreads) :返回线程池对象。(创建的是有界线程池,也就是池中的线程个数可以指定最大数量)

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

Runnable实现类代码:

public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我要一个教练");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("教练来了: " + Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教练回到了游泳池");
}
}

线程池测试类:

public class Demo{
public static void main(String[] args) {
// 创建线程池对象
ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
// 创建Runnable实例对象
MyRunnable r = new MyRunnable();
//自己创建线程对象的方式
// Thread t = new Thread(r);
// t.start(); ---> 调用MyRunnable中的run()
// 从线程池中获取线程对象,然后调用MyRunnable中的run()
service.submit(r);
// 再获取个线程对象,调用MyRunnable中的run()
service.submit(r);
service.submit(r);
// 注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。
// 将使用完的线程又归还到了线程池中
// 关闭线程池
//service.shutdown();
}
}

java_线程、同步、线程池的更多相关文章

  1. 线程同步&线程池

    线程同步&线程池 线程同步 线程不同步会出现的问题: 当多个线程操作同一资源时,会出现重复操作和和操作不存在的资源的问题,为了规避这一问题就需要线程的同步操作来实现资源的共同使用. 线程同步: ...

  2. 线程:主线程、子线程 同步线程、异步线程 单线程、多线程 System.Threading与System.Windows.Threading

    入门-------------------------------------------------------------------------------- 概述与概念    一个C#程序开始 ...

  3. Python并发编程-线程同步(线程安全)

    Python并发编程-线程同步(线程安全) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 线程同步,线程间协调,通过某种技术,让一个线程访问某些数据时,其它线程不能访问这些数据,直 ...

  4. Qt QThread 线程创建,线程同步,线程通信 实例

    1.  继承QThread, 实现run()方法, 即可创建线程. 2. 实例1 代码 myThread.h #ifndef MYTHREAD_H #define MYTHREAD_H #includ ...

  5. C#线程同步--线程通信

    问题抽象:当某个操作的执行必须依赖于另一个操作的完成时,需要有个机制来保证这种先后关系.线程通信方案:ManualResetEventSlim.ManualResetEvent.AutoResetEv ...

  6. 细说C#多线程那些事 - 线程同步和多线程优先级

    上个文章分享了一些多线程的一些基础的知识,今天我们继续学习. 一.Task类 上次我们说了线程池,线程池的QueueUserWorkItem()方法发起一次异步的线程执行很简单 但是该方法最大的问题是 ...

  7. C#并行编程(6):线程同步面面观

    理解线程同步 线程的数据访问 在并行(多线程)环境中,不可避免地会存在多个线程同时访问某个数据的情况.多个线程对共享数据的访问有下面3种情形: 多个线程同时读取数据: 单个线程更新数据,此时其他线程读 ...

  8. Linux多线程编程——多线程与线程同步

    多线程 使用多线程好处: 一.通过为每种事件类型的处理单独分配线程,可以简化处理异步事件的代码,线程处理事件可以采用同步编程模式,启闭异步编程模式简单 二.方便的通信和数据交换 由于进程之间具有独立的 ...

  9. java SE学习之线程同步(详细介绍)

           java程序中可以允许存在多个线程,但在处理多线程问题时,必须注意这样一个问题:               当两个或多个线程同时访问同一个变量,并且一些线程需要修改这个变量时,那么这个 ...

  10. Java之线程,常用方法,线程同步,死锁

    1, 线程的概念 进程与线程 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程.(进程是资源分配的最小单位) 线程:同一类线程共享代码和数据 ...

随机推荐

  1. Mariadb之主从复制的读写分离

    首先我们来回顾下代理的概念,所谓代理就是指的是一端面向客户端,另外一端面向服务端,代理客户端访问服务端,我们把这种代理叫正向代理:代理服务端响应客户端我们叫做反向代理,这个我们在之前nginx系列博客 ...

  2. bzoj2843极地旅行社

    bzoj2843极地旅行社 题意: 一些点,每个点有一个权值.有三种操作:点与点连边,单点修改权值,求两点之间路径上点的权值和(需要判输入是否合法) 题解: 以前一直想不通为什么神犇们的模板中LCT在 ...

  3. bzoj4631踩气球

    bzoj4631踩气球 题意: 有一个序列和一个区间集合,每次将序列中的一个数-1,求此时集合里有多少个区间和为0.序列大小≤100000,区间数≤100000,操作数≤100000. 题解: 此题解 ...

  4. 循序渐进VUE+Element 前端应用开发(18)--- 功能点管理及权限控制

    在一个业务管理系统中,如果我们需要实现权限控制功能,我们需要定义好对应的权限功能点,然后在界面中对界面元素的功能点进行绑定,这样就可以在后台动态分配权限进行动态控制了,一般来说,权限功能点是针对角色进 ...

  5. JVM详解之:运行时常量池

    目录 简介 class文件中的常量池 运行时常量池 静态常量详解 String常量 数字常量 符号引用详解 String Pool字符串常量池 总结 简介 JVM在运行的时候会对class文件进行加载 ...

  6. Reface.AppStarter 基本示例

    Reface.AppStarter 向应用层提供以下几项 核心 功能 以模块化组织你的应用程序 自动注册组件至 IOC 容器 自动映射配置文件至配置类 在模块定义类中额外追加组件至 IOC 容器 在模 ...

  7. Active Directory - Creating users via PowerShell

    Method1: Create a user by executing the following PowerShell Script. New-ADUser -name 'Michael Jorda ...

  8. Python Ethical Hacking - WEB PENETRATION TESTING(5)

    Guessing Login Information on Login Pages Our target website: http://10.0.0.45/dvwa/login.php #!/usr ...

  9. JVM——内存区域:运行时数据区域详解

    关注微信公众号:CodingTechWork,一起学习进步. 引言   我们经常会被问到一个问题是Java和C++有何区别?我们除了能回答一个是面向对象.一个是面向过程编程以外,我们还会从底层内存管理 ...

  10. 团队转型,Scrum与DevOps要如何取舍?

    团队在践行敏捷的过程中,会有多种选择:Scrum.XP.Kanban.Crystal.精益生产.规模化敏捷等,其中最流行的敏捷开发方法当属Scrum.正因如此,大部分人对其产生了刻板印象:认为敏捷就是 ...