目录
1.直接初始化Thead类,实现Runnable接口
2.继承Thread类
3.实现callable接口
4.使用线程池创建线程
查看Thread类源码可以发现,有下面这么一个构造参数,target是线程启动的时候要调用的方法(Runnable接口中有个run方法),如果为空,那这个类的运行方法什么都不做
那我们是否可以直接初始化线程,然后调用start方法启动呢
答案是可以的
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
测试代码如下
public static void main(String args[]){
new Thread(new Runnable(){
@Override
public void run() {
System.out.println("test create thread by Runable");
}
}).start();
}
Connected to the target VM, address: '127.0.0.1:57009', transport: 'socket'
test create thread by Runable
Disconnected from the target VM, address: '127.0.0.1:57009', transport: 'socket'
当然我们也可以写一个类去实现Runnable接口
测试代码如下
class Demo implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String args[]) {
Demo d = new Demo();
Thread thread1 = new Thread(d,"first thread");
Thread thread2 = new Thread(d,"second thread");
thread1.start();
thread2.start();
}
Connected to the target VM, address: '127.0.0.1:65445', transport: 'socket'
second thread
first thread
Disconnected from the target VM, address: '127.0.0.1:65445', transport: 'socket'
上面这种方式只new了一个Demo,所以thread1和thread2会共享类中的局部变量
我们测试下这种方式共享局部变量的效果,可以仔细看看count这个变量的变化,每个线程不是10->1这么变化的,说明count这个变量被2个线程共享了
class Demo implements Runnable{
private int count =10;
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+count--);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String args[]) {
Demo d = new Demo();
Thread thread1 = new Thread(d,"first thread");
Thread thread2 = new Thread(d,"second thread");
thread1.start();
thread2.start();
}
Connected to the target VM, address: '127.0.0.1:51427', transport: 'socket'
first thread10
second thread9
first thread8
second thread7
second thread6
first thread5
second thread4
first thread3
first thread2
second thread2
first thread1
second thread1
second thread0
first thread-1
second thread-2
first thread-3
second thread-4
first thread-5
second thread-6
first thread-7
Disconnected from the target VM, address: '127.0.0.1:51427', transport: 'socket'
Process finished with exit code 0
如果我们不想共享类中的变量,那只能初始化2个Demo类,代码如下
public static void main(String args[]) {
Demo d1 = new Demo();
Demo d2 = new Demo();
Thread thread1 = new Thread(d1,"first thread");
Thread thread2 = new Thread(d2,"second thread");
thread1.start();
thread2.start();
}
Connected to the target VM, address: '127.0.0.1:65346', transport: 'socket'
first thread10
second thread10
first thread9
second thread9
second thread8
first thread8
first thread7
second thread7
first thread6
second thread6
first thread5
second thread5
first thread4
second thread4
first thread3
second thread3
first thread2
second thread2
first thread1
second thread1
Disconnected from the target VM, address: '127.0.0.1:65346', transport: 'socket'
前面我们通过初始化Thread类的方式创建线程,那我们是否可以新建一个类继承Thread类,然后创建线程呢,那肯定是可以的
测试代码如下
class Demo extends Thread{
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String args[]) {
Demo d = new Demo();
Thread thread1 = new Thread(d,"create thread by extends Thread");
thread1.start();
}
Connected to the target VM, address: '127.0.0.1:64623', transport: 'socket'
create thread by extends Thread
Disconnected from the target VM, address: '127.0.0.1:64623', transport: 'socket'
A task that returns a result and may throw an exception. Implementors define a single method with no arguments called call. The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread. A Runnable, however, does not return a result and cannot throw a checked exception.(返回结果并可能抛出异常的任务。实现者定义了一个不带参数的方法,称为call。 Callable接口与Runnable接口类似,两者都是为其实例可能由另一个线程执行的类设计的。然而,Runnable不返回结果,也不能抛出检查异常。)
我们查看jdk中callable类的注释,可以知道几个关键的信息
1.Callable接口和Runnable接口类似,都是为其他线程执行实例而设计的
2.Callable可以接受结果
3.Callable可以检查异常
通过jdk的FutureTask类,我们可以看到其实现了RunableFuture接口,然后这个接口是继承的Runable
我们来看一下他的源码
我们可以通过这个方法来实例化FutureTask
Creates a FutureTask that will, upon running, execute the given Callable. Params: callable – the callable task Throws: NullPointerException – if the callable is null
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
然后通过get方法获取返回结果
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
Returns result or throws exception for completed task. Params: s – completed state value
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
测试代码如下
class Demo implements Callable{
@Override
public Object call() throws Exception {
int sum=0;
for(int i=0;i<10;i++){
sum=sum+i;
Thread.sleep(300);
}
System.out.println("sum:"+sum);
return sum;
}
}
public static void main(String args[]) throws Exception{
System.out.println("time1:"+new Date());
Demo d = new Demo();
FutureTask futureTask = new FutureTask(d);
Thread thread = new Thread(futureTask);
thread.start();
System.out.println("time2:"+new Date());
Object sum = futureTask.get(5,TimeUnit.SECONDS);
System.out.println("time3:"+new Date());
System.out.println(sum);
}
Connected to the target VM, address: '127.0.0.1:60755', transport: 'socket'
time1:Fri Mar 11 17:54:19 CST 2022
time2:Fri Mar 11 17:54:19 CST 2022
sum:45
time3:Fri Mar 11 17:54:22 CST 2022
45
Disconnected from the target VM, address: '127.0.0.1:60755', transport: 'socket'
我们可以发现time2和time3之间间隔了3s,然后获得了结果
线程池创建线程的优点
Thread pools address two different problems: they usually provide improved performance when executing large numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and managing the resources, including threads, consumed when executing a collection of tasks. Each ThreadPoolExecutor also maintains some basic statistics, such as the number of completed tasks.(线程池解决两个不同的问题:它们通常在执行大量异步任务时提高性能,因为减少了每个任务的调用开销,并且它们提供了一种方法来限制和管理执行任务集合时消耗的资源(包括线程)。每个ThreadPoolExecutor还维护一些基本的统计信息,比如完成任务的数量。)
1.使用ThreadPoolExecutor创建线程.这种方式比较灵活
Params:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
下面我们来测试一下,这里使用callable,当然也可以使用runnable
class Demo implements Callable{
@Override
public Object call() throws Exception {
int sum=0;
for(int i=0;i<10;i++){
sum=sum+i;
Thread.sleep(300);
}
System.out.println("sum:"+sum);
return sum;
}
}
public static void main(String args[]) throws Exception{
ThreadPoolExecutor pool = new ThreadPoolExecutor(10,20,60L,TimeUnit.SECONDS,new SynchronousQueue<>(), new ThreadFactoryBuilder().setNameFormat("test thread pool").build(),new ThreadPoolExecutor.AbortPolicy());
Demo d = new Demo();
Future f = pool.submit(d);
System.out.println("当前时间:"+new Date());
Object result = f.get(5L,TimeUnit.SECONDS);
System.out.println("当前时间:"+new Date()+"==结果:"+result);
}
当前时间:Mon Mar 14 11:28:42 CST 2022
sum:45
当前时间:Mon Mar 14 11:28:45 CST 2022==结果:45
2.使用Executors创建线程池
这种方法比较简单,其实就是对上面的方法进行了一些封装,建议使用上面的方法,因为可以看到具体的参数
Executorst提供了几个常见构造方法
我们来找一个这个类的构造参数看一下
Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue. At any point, at most nThreads threads will be active processing tasks. If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available. If any thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks. The threads in the pool will exist until it is explicitly shutdown.
Params:
nThreads – the number of threads in the pool
Returns:
the newly created thread pool
Throws:
IllegalArgumentException – if nThreads <= 0
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
测试一下
class Demo implements Runnable{
@Override
public void run() {
int sum=0;
for(int i=0;i<10;i++){
sum=sum+i;
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("sum:"+sum);
}
}
public static void main(String args[]) throws Exception{
ExecutorService executorService = Executors.newFixedThreadPool(10);
Demo d = new Demo();
executorService.submit(d);
}
Connected to the target VM, address: '127.0.0.1:55962', transport: 'socket'
sum:45
Disconnected from the target VM, address: '127.0.0.1:55962', transport: 'socket'