java学习
一.多线程
这里先来介绍一下创建多线程的几种方法。
1.扩展java.lang.Thread类
我们可以创建一个类,继承Thread,并且重写里面的run方法,将这个线程要去做的事写在run方法里面,在主程序实例化一个对象,在通过start启动它,这样就会创建另外一个子线程去执行run方法里面的功能。
public static void main(String[] args) {
Thread t1=new Thread1("A");
Thread t2=new Thread1("B");
Thread t3=new Thread1("C");
t1.start();
t2.start();
t3.start();
}
public class Thread1 extends Thread{
private String name;
public Thread1(String name)
{
this.name=name;
}
@Override
public void run() {
for(int i=1;i<=3;i++)
{
System.out.println(name+"线程执行:"+i);
}
}
}
C线程执行:1
A线程执行:1
B线程执行:1
A线程执行:2
C线程执行:2
A线程执行:3
B线程执行:2
C线程执行:3
B线程执行:3
可以看到,我们创建的三个子线程的任务交替进行。
为了看的更清楚,我们可以加点提示。
public static void main(String[] args) {
System.out.println("主线程执行");
Thread t1=new Thread1("A");
Thread t2=new Thread1("B");
Thread t3=new Thread1("C");
t1.start();
t2.start();
t3.start();
System.out.println("主线程结束");
}
主线程执行
主线程结束
B线程执行:1
A线程执行:1
C线程执行:1
A线程执行:2
B线程执行:2
A线程执行:3
C线程执行:2
B线程执行:3
C线程执行:3
为什么主线程开始和结束都最快呢,因为主线程执行到子线程的创建不会停下,这也是多线程的优势,如果不加多线程,主线程会为了执行某个任务而停下,所以主线程会执行很快,子线程是主线程结束后才来时执行的,我们也可以知道,运行时会将所有线程的任务执行完才会停止。
2.实现java.lang.Runnable接口
相比于上一个方法,当前方式创建子线程更具有优势。
下面我们看看实现方式
public class Thread1 implements Runnable{
private String name;
public Thread1(String name)
{
this.name=name;
}
@Override
public void run() {
for(int i=1;i<=3;i++)
{
System.out.println(name+"线程执行:"+i);
}
}
}
public static void main(String[] args) {
System.out.println("主线程执行");
Thread1 t1=new Thread1("A");
Thread1 t2=new Thread1("B");
Thread1 t3=new Thread1("C");
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
System.out.println("主线程结束");
}
主线程执行
主线程结束
A线程执行:1
B线程执行:1
C线程执行:1
B线程执行:2
A线程执行:2
B线程执行:3
C线程执行:2
A线程执行:3
C线程执行:3
3.实现Callable接口
相比于前面两种方法,实现Callable接口可以有返回值,还需要借助FutureTask类,去获取返回结果。
实现方法:
public class Thread1 implements Callable<Integer> {
private int n;
public Thread1(int n)
{
this.n=n;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= n; i++) {
sum+=i;
}
System.out.println(1+"-"+n+"之间的和:"+sum);
return sum;
}
}
public static void main(String[] args) throws Exception {
System.out.println("主线程执行");
Thread1 t1=new Thread1(100);
FutureTask<Integer> task=new FutureTask<>(t1);
new Thread(task).start();
System.out.println("主线程结束");
}
主线程执行
主线程结束
1-100之间的和:5050
我们还可以调用FutureTask对象的get()方法,去回去返回值。、
4.线程池
这里搭配Runnable使用
public class Thread1 implements Runnable{
@Override
public void run() {
for(int i=1;i<=3;i++)
{
System.out.println(Thread.currentThread()+"线程执行:"+i);
}
}
}
public static void main(String[] args) throws Exception {
System.out.println("主线程执行");
ThreadPoolExecutor tpool=new ThreadPoolExecutor(3,5,
4,TimeUnit.SECONDS,new LinkedBlockingQueue<>(10) );
Thread1 r1=new Thread1();
tpool.execute(r1);
tpool.execute(r1);
tpool.execute(r1);
tpool.execute(r1);
tpool.shutdown();
System.out.println("主线程结束");
}
主线程执行
主线程结束
Thread[pool-1-thread-1,5,main]线程执行:1
Thread[pool-1-thread-2,5,main]线程执行:1
Thread[pool-1-thread-3,5,main]线程执行:1
Thread[pool-1-thread-2,5,main]线程执行:2
Thread[pool-1-thread-1,5,main]线程执行:2
Thread[pool-1-thread-2,5,main]线程执行:3
Thread[pool-1-thread-3,5,main]线程执行:2
Thread[pool-1-thread-1,5,main]线程执行:3
Thread[pool-1-thread-2,5,main]线程执行:1
Thread[pool-1-thread-3,5,main]线程执行:3
Thread[pool-1-thread-2,5,main]线程执行:2
Thread[pool-1-thread-2,5,main]线程执行:3
ThreadPoolExecutor里面的参数
依次是开始的线程数,最大的线程数(-开始的线程数为预备线程数),线程存活时间(空闲多久后销毁),时间格式,工作队列。
5.总结
多线程的特点和优势:
-
并发执行:多线程允许程序的多部分(线程)在同一时间片段内并行执行,从而实现并发。
-
资源共享:多个线程可以共享相同的内存地址空间,这使得数据共享和通信更加容易和高效。
-
更高的性能:在多核或多处理器系统中,多线程可以充分利用硬件资源,提高程序的执行效率。
-
提高响应性:通过将阻塞或耗时的任务放在单独的线程中执行,可以确保用户界面的响应性。
多线程的挑战:
-
同步问题:多个线程访问和修改共享数据可能导致数据不一致或冲突,需要使用同步机制如互斥锁、信号量等来解决。
-
死锁和竞态条件:不恰当地使用同步机制可能导致死锁或竞态条件,这是多线程编程中常见的问题。
-
调试和测试:多线程程序的调试和测试通常比单线程程序更加困难,因为需要考虑到各种可能的执行路径和并发条件。
多线程的应用场景:
-
图形用户界面(GUI)应用程序:如浏览器、文本编辑器等,通过多线程确保用户界面的流畅和响应。
-
服务器和网络编程:如Web服务器、数据库服务器等,通过多线程处理并发请求。
-
并行计算和数据处理:如科学计算、图像处理、机器学习等,通过多线程提高计算效率。
总的来说,多线程是一种强大的编程模型,它可以提高程序的性能和响应速度,但同时也带来了一些复杂性和挑战,需要开发者有一定的经验和技巧来有效地应用。