线程对共享数据修改的部分,必须考虑是否线程安全!!!
并发编程的目的是为了能提高程序的执行效率,提高运行速度,但是并发编程有时候还会遇到很多问题,如:内存泄漏,上下文切换,线程安全,死锁等问题.
并发编程的三要素:
解决多线程安全的办法:
在执行程序的时候,为了提供性能,处理器和编译器常常会对指令进行重排序,但是不能随便进行重排序,需要满足两个条件
重排序不会影响单线程环境的执行结果,但是会破坏多线程的执行语意
当前任务在执行完CPU时间片后切换到另一个任务之前会先保存自己的状态,以便下次时间片轮转再次回到这个任务的时候,可以再次加载这个任务的状态.任务从保存到再加载的过程就是一次上下文切换
Linux相比于其他操作系统有很多优点,其中有一项就是:其上下文切换和模式切换的时间消耗非常小
最明显的区别是:用户线程结束,JVM退出,不管这个时候还有没有守护线程运行.守护线程不会影响JVM的退出.
注意事项:
继承Thread类
实现Runnable接口
实现Callable接口
使用Executors工具类创建线程池
继承自Thread类
步骤:
1. 定义一个Thread类的子类,重写run()方法,将相关操作实现,run()方法就是线程要执行的业务逻辑方法;
1. 创建自定义的线程子类对象
1. 调用子类实例的start()方法来启动线程
public class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +
" run()方法正在执行...");
}
}
public class TheadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println(Thread.currentThread().getName() +
" main()方法执行结束");
步骤:
1. 定义一个类实现Runnable接口,并重写run()方法
1. 创建MyRunnable的实例myRunnable,以myRunnable的对象作为target创建Thread对象,该Thread对象才是最终的线程对象
1. 调用线程对象的start()方法
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +
" run()方法执行中...");
}
}
public class RunnableTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
System.out.println(Thread.currentThread().getName() +
" main()方法执行完成");
}
}
步骤:
1. 创建实现Callable接口的类MyCallable
1. 以MyCallable类的实例为参数创建FutureTask对象
1. 将FutureTask对象作为参数创建Thread对象
1. 调用线程对象的start()方法
public class MyCallable implements Callable {
@Override
public Integer call() {
System.out.println(Thread.currentThread().getName() +
" call()方法执行中...");
return 1;
}
}
public class CallableTest {
public static void main(String[] args) {
FutureTask futureTask = new
FutureTask(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
try {
Thread.sleep(1000);
System.out.println("返回结果 " + futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() +
" main()方法执行完成");
}
}
Executors提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口
主要有newFixedThreadPool,newCachedThreadPool,newSingleThreadExecutor,newScheduleThreadPool
这四种线程池
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +
" run()方法执行中...");
}
}
public class SingleThreadExecutorTest {
public static void main(String[] args) {
ExecutorService executorService =
Executors.newSingleThreadExecutor();
MyRunnable runnableTest = new MyRunnable();
for (int i = 0; i < 5; i++) {
executorService.execute(runnableTest);
}
System.out.println("线程任务开始执行");
executorService.shutdown();
}
}
相同点:
主要区别:
多线程工作:new 一个Thread类,线程就会进入新建状态;调用start()方法,会启动一个线程并使线程进入就绪状态;当分配到时间片后就可以开始运行了.start()会执行线程的准备工作,然后自动调用run()方法执行其里面的内容
如果直接调用run()方法,会把run()方法当成是一个main线程下的普通方法去执行,并不会在某个线程中执行它,不属于多线程的工作
Callable接口类似于Runnable,但是Runnable接口的run()方法不会返回结果,并且无法抛出返回结果的异常;而Callable接口的功能更强大一些,call()方法有返回值,这个返回值可以被Future拿到(Future可以拿到异步执行任务的返回值),并且可以抛出异常获得异常信息
Future接口表示异步任务,是一个可能还没有完成的异步任务的结果.所以说Callable用于产生结果,Future用于获取结果
什么是Callable和Future?
Callable接口类似于Runnable,但是Runnable接口的run()方法不会返回结果,并且无法抛出返回结果的异常;而Callable接口的功能更强大一些,call()方法有返回值,这个返回值可以被Future拿到(Future可以拿到异步执行任务的返回值),并且可以抛出异常获得异常信息
Future接口表示异步任务,是一个可能还没有完成的异步任务的结果.所以说Callable用于产生结果,Future用于获取结果