Java 多线程,Java 四种方式创建线程,实现Runnable 接口和继承 Thread类的区别
Java 多线程,Java创建线程4种方式,Java 四种方式创建线程
实现Runnable 接口和继承 Thread类的区别
================================
?Copyright 蕃薯耀 2021-04-16
https://www.cnblogs.com/fanshuyao/
一、Java创建线程的四种方式
1、继承 Thread类
public class Threadable extends Thread{ @Override public void run() { System.out.println("Threadable 当前线程是:" + Thread.currentThread().getName()); } }
2、实现 Runnable 接口
public class ThreadRun implements Runnable{ @Override public void run() { System.out.println("ThreadRun 当前线程是:" + Thread.currentThread().getName()); } }
实现Runnable 接口和继承 Thread类的区别:
- synchronized(this)中使用this时,this是当前对象,继承 Thread类的方式创建多个线程时,this是不一样的,会出问题。实现Runnable 接口因为只有一个,所以没问题。
- synchronized(obj):可以在线程类创建一个obj对象,表示线程共用对象,这样给加锁的对象是同一个。需要注意的是:实现Runnable接口创建的对象不用加static,继承 Thread类创建的对象必须加static,不然对象就不是同一个。
- synchronized(XXX.class):使用类,类只有一个,能解决synchronized(obj)的问题(继承 Thread类创建的对象必须加static)。在Java中,任何东西都被看作对象,所以类也是可以的。
所以一般情况,建设使用实现Runnable 接口的方式,避免继承Thread存在的问题。
3、实现 Callable
import java.util.concurrent.Callable; public class ThreadCallable implements Callable
和实现 Runnable 接口的区别是:
- Callable可以往外抛异常
- Callable有返回值
4、使用线程池创建
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadTest5Executors { public static void main(String[] args) { ThreadRunData threadRun = new ThreadRunData(); //创建线程池,池里有10个线程 ExecutorService executorService = Executors.newFixedThreadPool(10); executorService.execute(threadRun);//execute是没返回的 executorService.execute(threadRun);//execute是没返回的 executorService.submit(threadRun);//submit是有返回的 executorService.submit(threadRun);//submit是有返回的 //executorService.submit(task);//submit是有返回的,如果使用Callable且有返回值,则需要使用submit //关闭线程池 executorService.shutdown(); } }
二、Java 多线程 Synchronized 关键字使用
synchronized(object对象)
- synchronized(this):this表示调用的当前对象
- synchronized(obj):可以在线程类创建一个obj对象,表示线程共用对象,这样给加锁的对象是同一个。需要注意的是:实现Runnable接口创建的对象不用加static,继承 Thread类创建的对象必须加static,不然对象就不是同一个。
- synchronized(XXX.class):使用类,类只有一个,能解决synchronized(obj)的问题(继承 Thread类创建的对象必须加static)。在Java中,任何东西都被看作对象,所以类也是可以的。
1、通过实现Runnable接口创建线程类
public class ThreadRunData implements Runnable{ private int ticket = 30; @Override public void run() { while(true) { synchronized(this) { if(ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ThreadRunData " + Thread.currentThread().getName() + ":卖出第" + ticket + "票"); ticket--; }else { System.out.println("ThreadRunData " + Thread.currentThread().getName() + ":票已售完"); break; } } } } }
2、多线程共同卖票,共同的数据是ticket
public class ThreadTest2 { public static void main(String[] args) { ThreadRunData threadRun = new ThreadRunData(); Thread thread = new Thread(threadRun); thread.setName("线程一"); thread.start(); Thread thread2 = new Thread(threadRun); thread2.setName("线程二"); thread2.start(); Thread thread3 = new Thread(threadRun); thread3.setName("线程三"); thread3.start(); Thread thread4 = new Thread(threadRun); thread4.setName("线程四"); thread4.start(); } }
结果:
未加synchronized关键字前:
存在问题:
- 票重复卖
- 票超量卖
ThreadRunData 当前线程是:线程二,卖出第30票 ThreadRunData 当前线程是:线程三,卖出第30票 ThreadRunData 当前线程是:线程四,卖出第30票 ThreadRunData 当前线程是:线程一,卖出第30票 ThreadRunData 当前线程是:线程四,卖出第26票 ThreadRunData 当前线程是:线程一,卖出第26票 ThreadRunData 当前线程是:线程二,卖出第26票 ThreadRunData 当前线程是:线程三,卖出第26票 ThreadRunData 当前线程是:线程二,卖出第22票 ThreadRunData 当前线程是:线程三,卖出第22票 ThreadRunData 当前线程是:线程一,卖出第22票 ThreadRunData 当前线程是:线程四,卖出第22票 ThreadRunData 当前线程是:线程四,卖出第18票 ThreadRunData 当前线程是:线程二,卖出第18票 ThreadRunData 当前线程是:线程一,卖出第18票 ThreadRunData 当前线程是:线程三,卖出第18票 ThreadRunData 当前线程是:线程三,卖出第14票 ThreadRunData 当前线程是:线程二,卖出第14票 ThreadRunData 当前线程是:线程一,卖出第14票 ThreadRunData 当前线程是:线程四,卖出第14票 ThreadRunData 当前线程是:线程二,卖出第10票 ThreadRunData 当前线程是:线程三,卖出第10票 ThreadRunData 当前线程是:线程四,卖出第10票 ThreadRunData 当前线程是:线程一,卖出第10票 ThreadRunData 当前线程是:线程四,卖出第6票 ThreadRunData 当前线程是:线程二,卖出第6票 ThreadRunData 当前线程是:线程三,卖出第6票 ThreadRunData 当前线程是:线程一,卖出第6票 ThreadRunData 当前线程是:线程三,卖出第2票 ThreadRunData 当前线程是:线程一,卖出第2票 ThreadRunData 当前线程是:线程一,票已售完 ThreadRunData 当前线程是:线程二,卖出第2票 ThreadRunData 当前线程是:线程二,票已售完 ThreadRunData 当前线程是:线程四,卖出第2票 ThreadRunData 当前线程是:线程四,票已售完 ThreadRunData 当前线程是:线程三,卖出第-2票 ThreadRunData 当前线程是:线程三,票已售完
加了synchronized关键字之后:
解决了上面的问题。
ThreadRunData 当前线程是:线程一,卖出第30票
ThreadRunData 当前线程是:线程一,卖出第29票
ThreadRunData 当前线程是:线程一,卖出第28票
ThreadRunData 当前线程是:线程一,卖出第27票
ThreadRunData 当前线程是:线程一,卖出第26票
ThreadRunData 当前线程是:线程一,卖出第25票
ThreadRunData 当前线程是:线程一,卖出第24票
ThreadRunData 当前线程是:线程一,卖出第23票
ThreadRunData 当前线程是:线程一,卖出第22票
ThreadRunData 当前线程是:线程一,卖出第21票
ThreadRunData 当前线程是:线程一,卖出第20票
ThreadRunData 当前线程是:线程一,卖出第19票
ThreadRunData 当前线程是:线程一,卖出第18票
ThreadRunData 当前线程是:线程一,卖出第17票
ThreadRunData 当前线程是:线程一,卖出第16票
ThreadRunData 当前线程是:线程一,卖出第15票
ThreadRunData 当前线程是:线程一,卖出第14票
ThreadRunData 当前线程是:线程一,卖出第13票
ThreadRunData 当前线程是:线程一,卖出第12票
ThreadRunData 当前线程是:线程四,卖出第11票
ThreadRunData 当前线程是:线程四,卖出第10票
ThreadRunData 当前线程是:线程四,卖出第9票
ThreadRunData 当前线程是:线程三,卖出第8票
ThreadRunData 当前线程是:线程三,卖出第7票
ThreadRunData 当前线程是:线程三,卖出第6票
ThreadRunData 当前线程是:线程三,卖出第5票
ThreadRunData 当前线程是:线程三,卖出第4票
ThreadRunData 当前线程是:线程三,卖出第3票
ThreadRunData 当前线程是:线程二,卖出第2票
ThreadRunData 当前线程是:线程二,卖出第1票
ThreadRunData 当前线程是:线程二,票已售完
ThreadRunData 当前线程是:线程三,票已售完
ThreadRunData 当前线程是:线程四,票已售完
ThreadRunData 当前线程是:线程一,票已售完
三、Java 多线程 ReentrantLock 使用
ReentrantLock 锁和synchronized的区别:
- ReentrantLock要手动加锁和释放锁;synchronized关键字可以在代码块或者方法中,运行完后自动释放锁,避免忘记释放锁,形成死锁
- ReentrantLock可以尝试获取锁,在多次获取后,可以终止并释放锁;synchronized会一直等待。
一般情况,建议还是使用synchronized吧。
1、线程中使用ReentrantLock 加锁
import java.util.concurrent.locks.ReentrantLock; public class ThreadRunDataLock implements Runnable{ private int ticket = 30; private ReentrantLock reentrantLock = new ReentrantLock(); @Override public void run() { while(true) { try { //加锁 reentrantLock.lock(); if(ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ThreadRunData " + Thread.currentThread().getName() + ":卖出第" + ticket + "票"); ticket--; }else { System.out.println("ThreadRunData " + Thread.currentThread().getName() + ":票已售完"); break; } }finally { //释放锁 reentrantLock.unlock(); } } } }
2、创建多个线程调用
public class ThreadTest4Lock { public static void main(String[] args) { ThreadRunDataLock threadRun = new ThreadRunDataLock(); Thread thread = new Thread(threadRun); thread.setName("线程一"); thread.start(); Thread thread2 = new Thread(threadRun); thread2.setName("线程二"); thread2.start(); Thread thread3 = new Thread(threadRun); thread3.setName("线程三"); thread3.start(); Thread thread4 = new Thread(threadRun); thread4.setName("线程四"); thread4.start(); } }
四、两个线程交替运行
1、线程类:交替运行
/** * 线程一、线程二交替卖票 * @author liqiongy * */ public class ThreadRunDataAlternate implements Runnable{ private int ticket = 30; @Override public void run() { while(true) { synchronized(this) { //唤醒一个其它在等待的线程。存在多个等待的线程只唤醒一个,因为只有2个线程,所以唤醒的一定是另一个 this.notify(); if(ticket > 0) { try { //线程睡眠,不释放锁和资源 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ThreadRunData " + Thread.currentThread().getName() + ":卖出第" + ticket + "票"); ticket--; try { //线程等待,释放锁和资源 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { System.out.println("ThreadRunData " + Thread.currentThread().getName() + ":票已售完"); break; } } } } }
2、创建两个线程测试:
public class ThreadTest3Alternate { public static void main(String[] args) { ThreadRunDataAlternate threadRun = new ThreadRunDataAlternate(); Thread thread = new Thread(threadRun); thread.setName("线程1"); thread.start(); Thread thread2 = new Thread(threadRun); thread2.setName("线程二"); thread2.start(); } }
3、结果:
ThreadRunData 线程1:卖出第30票
ThreadRunData 线程二:卖出第29票
ThreadRunData 线程1:卖出第28票
ThreadRunData 线程二:卖出第27票
ThreadRunData 线程1:卖出第26票
ThreadRunData 线程二:卖出第25票
ThreadRunData 线程1:卖出第24票
ThreadRunData 线程二:卖出第23票
ThreadRunData 线程1:卖出第22票
ThreadRunData 线程二:卖出第21票
ThreadRunData 线程1:卖出第20票
ThreadRunData 线程二:卖出第19票
ThreadRunData 线程1:卖出第18票
ThreadRunData 线程二:卖出第17票
ThreadRunData 线程1:卖出第16票
ThreadRunData 线程二:卖出第15票
ThreadRunData 线程1:卖出第14票
ThreadRunData 线程二:卖出第13票
ThreadRunData 线程1:卖出第12票
ThreadRunData 线程二:卖出第11票
ThreadRunData 线程1:卖出第10票
ThreadRunData 线程二:卖出第9票
ThreadRunData 线程1:卖出第8票
ThreadRunData 线程二:卖出第7票
ThreadRunData 线程1:卖出第6票
ThreadRunData 线程二:卖出第5票
ThreadRunData 线程1:卖出第4票
ThreadRunData 线程二:卖出第3票
ThreadRunData 线程1:卖出第2票
ThreadRunData 线程二:卖出第1票
ThreadRunData 线程1:票已售完
ThreadRunData 线程二:票已售完
五、Callable 实现线程
1、实现Callable接口定义线程类
import java.util.concurrent.Callable; public class ThreadCallable implements Callable{ @Override public Integer call() throws Exception { int sum= 0; //计算从1加到100 for(int i=1; i<=100; i++) { sum += i; } return sum; } }
方式一:普通线程实现
import java.util.concurrent.FutureTask; /** * Callable 实现线程 * */ public class ThreadTest7Callable { public static void main(String[] args) throws Exception{ //实现Callable的线程类 ThreadCallable callableThread = new ThreadCallable(); //封装回返对象 FutureTaskfuture = new FutureTask (callableThread); //启动线程 new Thread(future).start(); System.out.println("1加到100的和是:" + future.get()); } }
方式二:线程池实现
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * Callable 实现线程 * */ public class ThreadTest6Callable { public static void main(String[] args) throws Exception{ ThreadCallable thread = new ThreadCallable(); //创建线程池,池里有10个线程 ExecutorService executorService = Executors.newFixedThreadPool(10); Futurefuture = executorService.submit(thread);//submit是有返回的,如果使用Callable且有返回值,则需要使用submit System.out.println("1加到100的和是:" + future.get()); //关闭线程池 executorService.shutdown(); } }
(如果文章对您有所帮助,欢迎捐赠,^_^)
================================
?Copyright 蕃薯耀 2021-04-16
https://www.cnblogs.com/fanshuyao/