很多线程是模拟出来的,真正的多项成是指由多个CPU,即多核,如服务器。如果是模拟出来的多线程,只有一个CPU,在同一个时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错局。
线程就是独立的执行路径
在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程
main()称之为主线程,为系统的入口,用于执行整个程序
在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序不能人为干预
对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
线程会带来额外的开销,如CPU调度时间,并发控制开销
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
Thread class 继承Thread类(重点)
Runnable 实现Runnable接口(重点)
Callable 实现Callable接口(了解)
继承Thread类
子类继承Thread类具备多线程能力
启动线程:子对象.start()
不建议使用:避免OOP单继承局限性
实现Runnable接口
实现接口Runnable具有多线程能力
启动线程:传入目标对象+Thread对象.start()
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
真实对象和代理对象都要实现同一个接口
代理对象代理真实角色
好处:代理对象可以做很多真实对象做不了的事情,真实对象专注做自己的事情
函数式接口的定义:
任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
创建状态——就绪状态——运行状态——阻塞状态——死亡状态
//建议线程正常停止--->利用次数,不建议死循环
//建议使用标志位
//不要使用stop或者destroy
//模拟网络延时
Thread.sleep();
//作用:放大问题
sleep(时间)指定当前线程阻塞的毫秒数
sleep存在异常InterruptedException
sleep时间到达后线程进入就绪状态
sleep可以模拟网络延时,倒计时等
每一个对象都有一个锁,sleep不会释放锁
package cn.lucky.state;
?
import java.text.SimpleDateFormat;
import java.util.Date;
?
/**
* @author lucky
*/
public class TestSleep2{
?
public static void main(String[] args) {
//模拟倒计时
try {
tenDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
//打印当前时间
Date startTime = new Date(System.currentTimeMillis());
while(true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());//更新当前时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
?
public static void tenDown() throws InterruptedException {
int num =10;
while (true){
Thread.sleep(1000);
System.out.println(num--);
if(num<0)break;
}
}
}
?
Thread.yield();
//礼让不一定成功,看CPU心情
thread.join();//插队
getPriority();
setPriority();
优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看CPU的调度。
线程优先级的设定建议在start()调度之前
thread.setDaemon(true);//默认是false表示是用户线程,正常的线程都是用户线程
由于同一-进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized ,当-个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可. 存在以下问题:
一个线程持有锁会导致其他所有需要此锁的线程挂起;
在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒置,引起性能问题.
synchronized(Obj){}//synchronized同步锁
程序里面避免死锁
产生死锁的四个必要条件
互斥条件:一个资源每次只能被一个进程使用
请求与保持条件:一个进行因请求资源而阻塞时,对已获得的资源保持不放
不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
synchronized与Lock的对比
Lock是显式锁(手动开启和关闭锁,别忘记关闭锁) synchronized是隐式锁, 出了作用域自动释放
Lock只有代码块锁,synchronized有代码块锁和方法锁
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
优先使用顺序:
Lock >同步代码块(已经进入了方法体,分配了相应资源) >同步方法(在方法体之外)
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
好处:
提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程管理
corePoolSize: 核心池的大小
maximumPoolSize:最大线程数
keepAlive Time:线程没有任务时最多保持多长时间后会终止
使用线程池:
ExecutorService: 真正的线程池接口。常见子类ThreadPoolExecutor
void execute(Runnable command) :执行任务/命令,没有返回值,- 般用来执行Runnable
<T> Future<T> submit(Callable<T> task):执行任务,有返回值,-般又来执行Callable
void shutdown() :关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
package cn.lucky.gaoji;
?
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
?
/**
* @author lucky
*/
public class ThreadNew {
public static void main(String[] args) {
new MyThread1().start();
?
new Thread(new MyThread2()).start();
?
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
new Thread(futureTask).start();
?
try {
Integer integer = futureTask.get();
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
?
}
?
//1.继承Thread类
class MyThread1 extends Thread{
原文:https://www.cnblogs.com/lucky8991/p/14432390.html