java 线程池 同步_java_线程、同步、线程池

本文详细介绍了Java中创建线程的两种方式:继承Thread类和实现Runnable接口,并通过实例展示了如何启动线程。此外,文章还探讨了线程安全问题,包括同步代码块、同步方法以及Lock锁的使用,确保多线程环境下资源的正确访问。最后,讲解了线程池的概念及其优势,通过Executors创建线程池并提交任务执行。
摘要由CSDN通过智能技术生成

线程

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类来创建并启动多线程的步骤如下:

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

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

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

测试类:

public classDemo{public static voidmain(String[] args) {//创建自定义线程对象

MyThread mt = new MyThread("新线程");//开启新线程

mt.start();//在主方法中执行for循环

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

System.out.println("main线程正在执行" +i);

}

}

}

自定义线程类:

public class MyThread extendsThread {//定义指定线程名称的构造方法

publicMyThread(String name) {//调用父类的String参数的构造方法,指定线程的名称

super(name);

}/*** 重写run方法,完成该线程执行的逻辑*/@Overridepublic voidrun() {for (int i = 0; i < 10; i++) {

System.out.println(getName()+ "正在执行" +i);

}

}

}

流程图:

b14a0a0dc52e83c750301b81561cb627.png

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

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

创建线程方式二

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

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

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

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

测试类:

public classDemo {public static voidmain(String[] args) {//创建自定义类对象线程任务对象

MyRunnable mr = newMyRunnable();//创建线程对象

Thread t = new Thread(mr, "新线程");

t.start();for (int i = 0; i < 10; i++) {

System.out.println("main线程正在执行" +i);

}

}

}

自定义线程类:

public class MyRunnable implementsRunnable {

@Overridepublic voidrun() {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类所具有的优势:

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

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

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

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

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

public classDemo {public static voidmain(String[] args) {//使用匿名内部类方法;直接创建Thread类的子类对象

/** new Thread() { public void run() { for (int i = 0; i < 10; i++) {

* System.out.println("新线程正在执行" + i); } } }.start();*/

//使用匿名内部类方式;直接创建Runnable接口实现类对象

Runnable r = newRunnable() {public voidrun() {for (int i = 0; i < 10; i++) {

System.out.println("新线程正在执行" +i);

}

}

};newThread(r).start();for (int i = 0; i < 10; i++) {

System.out.println("main线程正在执行" +i);

}

}

}

线程安全

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

模拟卖50张电影票

public class Ticket implementsRunnable {private int ticket = 50;//买票操作

@Overridepublic voidrun() {//每个窗口买票操作,窗口永远开启

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 classDemo {public static voidmain(String[] args) {//创建线程任务对象

Ticket ticket = newTicket();//创建三个窗口卖票

Thread t1 = new Thread(ticket, "窗口一");

Thread t2= new Thread(ticket, "窗口二");

Thread t3= new Thread(ticket, "窗口三");

​//同时开始卖票

t1.start();

t2.start();

t3.start();

}

}

结果出现了这种现象:

d6ad187dc595e90f74bea68e9115df4a.png

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

线程同步

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

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

同步代码块

同步方法

锁机制

同步代码块

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

synchronized(同步锁){

需要同步操作的代码

}

同步锁注意事项1.锁对象可以是任意类型。2.多个线程对象要使用同一把锁。

同步代码块实现线程安全

public class Ticket implementsRunnable {private int ticket = 50;

Object lock= newObject();

​//买票操作

@Overridepublic voidrun() {//每个窗口买票操作,窗口永远开启

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 implementsRunnable {private int ticket = 50;//买票操作

@Overridepublic voidrun() {//每个窗口买票操作,窗口永远开启

while (true) {

sellTicket();

}

}//锁对象是谁调用这个方法就是谁,this

public synchronized voidsellTicket() {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 implementsRunnable {private int ticket = 50;

Lock lock= newReentrantLock();//买票操作

@Overridepublic voidrun() {//每个窗口买票操作,窗口永远开启

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();

}

}

}

线程池

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

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

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

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

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

线程池的使用

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

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

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

Runnable实现类代码:

public class MyRunnable implementsRunnable {

@Overridepublic voidrun() {

System.out.println("我要一个教练");try{

Thread.sleep(2000);

}catch(InterruptedException e) {

e.printStackTrace();

}

System.out.println("教练来了: " +Thread.currentThread().getName());

System.out.println("教我游泳,交完后,教练回到了游泳池");

}

}

线程池测试类:

public classDemo{public static voidmain(String[] args) {//创建线程池对象

ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象//创建Runnable实例对象

MyRunnable r = newMyRunnable();//自己创建线程对象的方式//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();

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值