01 线程的概念和创建.txt
UP 返回
视频:H:\视频教程\53套JAVA技术提升及架构精华课\41.第四十一套 Java并发编程原理与实战
1 可以在控制台通过jconsole命令查看java进程
相关书籍:
《Java并发编程实战》
《Java并发编程的艺术》 方腾飞
《深入理解Java虚拟机》 周志明
2 并发的缺点
安全性问题
活跃性问题(饥饿)
性能问题
多线程不一定是并发。因为即使只有一个cpu,也可以创建多线程,只不过这些线程并不是同时执行(但是线程切换的很快时,给人的感觉就是在并发)。真正的并发应该是多个线程同时进行,多个cpu就可以做到
线程与进程的区别:
进程:运行中的程序
进程是资源分配的基本单位;进程中包含多个线程,线程共享进程的资源;线程是处理器调度的基本单位
多线程不一定快,因为线程的切换也会浪费资源
线程状态转换参Java/02.线程中的描述
thread.setDaemon(true) 将线程设置为守护线程,这样线程会随着主线程的退出自动结束。这句话要写在start()之前。比如垃圾回收线程
stop方法废弃的原因:因为这种方式只会让线程无限等待下去,并不会释放它的锁和静态资源。现在推荐使用interrupt方法来使线程中断,相应写法是:
public static void main(String []args) {
MyThread thread1=new MyThread("线程1");
MyThread thread2=new MyThread("线程2");
thread1.start();
thread2.start();
thread1.interrupt(); //中断线程
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
// TODO Auto-generated method stub
while(!interrupted()) { //这里以后不要再使用while(true)来实现了,这种中断的方式更好
System.out.println(getName()+" is running");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
3 线程的创建
继承Thread类
实现Runnable接口
匿名内部类的方式
new Thread(){
}.start();
new Thread(new Runnable(){
}
).start();
如果两个方法都使用,即同时重写了接口和类的run方法,最终调用的是类的run。因为作为子类,接口中的任务只是被父类指向了,而多态调用的仍然是子类的方法
带返回值的线程
使用方法:
public class MyCallable implements Callable<Integer> {
public static void main(String[] args) throws Exception {
MyCallable myCallable = new MyCallable();
FutureTask<Integer> task = new FutureTask<Integer>(myCallable);
Thread t = new Thread(task);
t.start();
Integer result = task.get();
System.out.println("结果为:" + result);
}
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
System.out.println("计算中...");
return 1;
}
}
定时器(quartz 定时任务框架)
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// 实现定时任务
System.out.println("timer is running");
}
}, 0, 1000);// 规定定时任务立刻执行,并每隔一秒执行一次(还有很多其他构造方法,可以自己尝试)
线程池的实现
ExecutorService threadPool = Executors.newFixedThreadPool(10);
// ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName() + " is running");
}
});
}
Lambda表达式实现
public class LambdaTest {
public static void main(String[] args) {
List<Integer> values = Arrays.asList(10, 20, 30, 40);
int res = new LambdaTest().add(values);
System.out.println(res);
}
public int add(List<Integer> values) {
// parallelStream获取的流就是并行执行的
values.parallelStream().forEach(System.out::println);
// stream获取的流不是并行执行的。可以通过打印出的数字顺序看出来
values.stream().forEach(System.out::println);
// 如果并行还希望顺序是和开始一致的,可以使用forEachOrdered来遍历
values.parallelStream().forEachOrdered(System.out::println);
// 以上的代码只是为了验证parallelStream是并行的。下面这句才是实际的方法体
return values.parallelStream().mapToInt(a -> a).sum();
}
}
Spring实现多线程
创建一个maven项目,引入context依赖(参 D:\ProjectCodes\eclipse2019\testPro)
创建配置类Config
@Configuration
@ComponentScan("com.dm.test1")
@EnableAsync
public class Config {
}
创建异步类DemoService
@Service
public class DemoService {
@Async
public void method1() {
while (true) {
System.out.println("method1 run");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Async
public void method2() {
while (true) {
System.out.println("method2 run");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
创建运行类Run
public class Run {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
DemoService demoService = ac.getBean(DemoService.class);
demoService.method1();
demoService.method2();
}
}
可以看到方法交替异步运行
4 多线程的风险
4.1 活跃性问题
死锁 哲学家问
饥饿 低优先级长时间无法执行
饥饿与公平:
高优先级吞噬所有低优先级的CPU时间片
线程被永久堵塞在一个等待进入同步块的状态
等待的线程永远不被唤醒
如何尽量避免饥饿问题
设置合理的优先级
使用锁来代替synchronized
活锁 互相谦让,导致都无法执行
4.2 性能问题
4.3 线程安全问题
多线程环境下
多个线程共享一个资源
对资源进行非原子性操作
进入java项目目录,使用javap -verbose $XXXXX$1.class可以查看编译的字节码文件内容
DOWN 返回