一,多线程
1.多线程技术概述
1.1 线程与进程
进程:
1-是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
线程:
1-是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少与一个线程
2-线程实际上是在线程基础上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程
1.2 线程调度
分时调度:
-所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
抢占式调度:
1-优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),java使用的就是抢占式调度。
2-CPU使用抢占式调度模式在
1.3 同步与异步
同步:排队执行,效率低但是安全。
异步:同时执行,效率高但是数据不安全。
1.4 并发与并行
并发:指两个或多个事件在同一个时间段发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
1.5 执行过程图
注意
每一个线程都拥有自己的栈空间,但共用一份堆内存。
由一个线程执行的方法,那么这个方法也会执行在这个线程里面,也就是说也会在这个线程的栈内存里面执行。
1.6 线程阻塞(也就是耗时操作)
2.Thread(此类就是一个线程)
2.1 直接继承来实现多线程
public class Thread_1 {
public static void main(String[] args) {
MaThread maThread=new MaThread();
maThread.start();//通过start来启动这个线程
for (int i = 0; i < 10; i++) {
System.out.println("我是main线程"+i);
}
}
}
class MaThread extends Thread{
@Override
public void run() {//run方法就是线程要执行的方法
//这里的代码就是一个新的执行路径,且调用这个方法不能直接调用run方法
for (int i = 0; i < 10; i++) {
System.out.println("我是新的线程"+i);
}
}
}
2.2 匿名内部类实现多线程(也是继承关系)
public class Thread_2 {
public static void main(String[] args) {
new Thread(){//匿名内部类必须继承一个父类或则实现一个接口
//这就是继承了Thread()类
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我是新的线程"+i);
}
}
}.start();
for (int i = 0; i < 10; i++) {
System.out.println("我是旧的线程"+i);
}
}
}
3.Runnable (创建任务)
3.1 案列实现
public class Runnable_1 {
public static void main(String[] args) {
MyRunnable maThread= new MyRunnable();
Thread t=new Thread(maThread);
t.start();
for (int i = 0; i < 10; i++) {
System.out.println("我是新的线程"+i);
}
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我是旧的线程"+i);
}
}
}
4. 使用Runnable的优点(与Thread比)
Runnable是通过创建任务,然后给线程分配任务,
1,更适合多个线程同时执行相同任务的情况,并且可以避免单继承带来的局限性
2.任务与线程本身是分离的,提高了程序的健壮性。
3.后续学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程
5. 设置并获取线程的名称
Thread.currentThread().getName()//获取当前线程的名称
new Thread(线程任务对象,"新设置的名称").start();//设置线程名称并启动
6. 线程休眠sleep
sleep()方法是是Tread的静态方法,可以直接调用。
即:Tread.sleep(毫秒);
7. 线程的中断
一个线程是独立的执行路径,它是否结束应该由自身决定。
我们应该给线程一个标记,然后当遇到标记时,直接创建一个新的异常进入catch语句中,线程是否结束由catch中的语句决定
如:public class interrupt_1 {
public static void main(String[] args) {
Thread t1=new Thread(new MyRunnable2(),"子线程");
t1.start();
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
t1.interrupt();//给线程一个标记,当线程运到这个位置时,产生一个新的异常,然后直接进去catch
}
static class MyRunnable2 implements Runnable{
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//e.printStackTrace();
System.out.println("发现了中断标记,我们这个线程自动死亡");
return;//结束这个任务
}
}
}
}
}
8. 守护线程和用户线程
线程分为守护线程和用户线程
用户线程:当一个线程不包含任何的守护线程时,这个线程结束。
守护线程:守护用户线程的,当用户线程全部死亡,守护线程自动死亡
Thread t1=new Thread(new MyRunnable());
t1.setDaemon(true);//true表示是守护线程
t1.start();
注意:设置守护线程应该在启动线程之前
二,线程安全
1.线程安全1,同步代码块(隐式锁)
三个对象同时抢对象o这把锁,谁先抢到锁o,谁先执行,其余线程等待,直到抢到锁的线程执行结束,然后又重新开始抢
注意:谁先抢到第一把锁的线程,抢到第二把的机会就更高(这就叫回首掏)
public class synchronized_1 {
public static void main(String[] args) {
Runnable run=new MyRunnable();
new Thread(run,"一").start();
new Thread(run,"二").start();
new Thread(run,"三").start();
}
static class MyRunnable implements Runnable{
private int count=10;
Object o=new Object();
@Override
public void run() {
while (true){
synchronized (o){
if(count>0){
System.out.println("正在出票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,剩余票数"+count);
}else {
break;
}
}
}
}
}
}
2. 线程安全2-同步方法(隐式锁)
注意:同步方法上锁的是this,这个this就是调用这个方法的对象,在这里就是run对象
如果这个方法是静态的,那么上锁的就是方法类的.class
public class synchronized_2 {
public static void main(String[] args) {
Runnable run=new synchronized_1.MyRunnable();
new Thread(run,"一").start();
new Thread(run,"二").start();
new Thread(run,"三").start();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
while (true){
Boolean aa=say();
if (aa=false){
break;
}
}
}
public synchronized Boolean say(){
int count=10;
if (count>0){
System.out.println("正在出票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,剩余票数"+count);
}
return false;
}
}
}
3. 线程安全3-显示锁Lock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Lock_1 {
public static void main(String[] args) {
Runnable myLock=new MyLock();
new Thread(myLock).start();
new Thread(myLock).start();
new Thread(myLock).start();
}
}
class MyLock implements Runnable{
int count=10;
//创建一个锁
Lock l=new ReentrantLock();
@Override
public void run() {
while (true) {
l.lock();//开始锁住
if (count > 0) {
System.out.println("正在出票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功,剩余票数" + count);
} else if (count==0){
break;
}
l.unlock();//解锁
}
}
}
三, 带返回值得线程Callable
public class Callable1 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<Integer> ca=new MyCallable();//创建对象
FutureTask<Integer> fu=new FutureTask<>(ca);//创建任务
new Thread(fu).start();//创建线程运行任务
fu.get();//主线程停在这里等子线程执行完毕
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"我运行"+i+"次");
}
}
static public class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"我运行"+i+"次");
}
return 100;
}
}
}
四, 线程池
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容 器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
1.线程池的好处
1-降低资源消耗
2-提高响应速度
3-提高线程的可管理性
2.Java中的四种线程池 . ExecutorServic
2.1 缓存线程池
public class newCachedThreadPool {
public static void main(String[] args) {
ExecutorService service= Executors.newCachedThreadPool();//新建一个缓存线程池
//指挥线程池执行新的任务,以下是3个线程执行三个任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"嘻嘻");
}
}); service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"嘿嘿");
}
}); service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"哈哈");
}
});
}
}
结果:pool-1-thread-1嘻嘻
pool-1-thread-3哈哈
pool-1-thread-2嘿嘿
2.2 定长度线程池
public class newFixedThreadPool {
public static void main(String[] args) {
ExecutorService service= Executors.newFixedThreadPool(2);//定长度线程池,把线程池的个数定位2
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"嘻嘻");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"嘿嘿");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});service.execute(new Runnable() {//因为线程池中线程的个数为2,所以第三个线程只有等其中一个结束,才能执行
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"哈哈");
}
});
}
}
结果:
pool-1-thread-1嘻嘻
pool-1-thread-2嘿嘿
pool-1-thread-1哈哈
2.3 单线程线程池
public class newSingleThreadPool {
public static void main(String[] args) {
ExecutorService service= Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"嘻嘻");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"嘿嘿");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});service.execute(new Runnable() {//因为线程池中线程的个数为2,所以第三个线程只有等其中一个结束,才能执行
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"哈哈");
return;
}
});
}
}
结果:
pool-1-thread-1嘻嘻
pool-1-thread-1嘿嘿
pool-1-thread-1哈哈
2.4 周期定长线程池
//周期定长线程池
public class newScheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService service= Executors.newScheduledThreadPool(2);
/**
* 1 定时执行一次
* 参数1:定时执行的任务
* 参数2:时长数字
* 参数3:时长数字的时间单位,TimeUnit的常量指定
*/
/*service.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"嘿嘿");
}
},4, TimeUnit.SECONDS);//4秒后执行*/
/**
* 2 周期执行任务
* 参数1:任务
* 参数2:延迟时长数字(第一次在什么时间以后)
* 参数3:周期时长数字(每隔多久执行一次)
* 参数4:时长数字单位
*/
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"嘿嘿");
}
},4,2,TimeUnit.SECONDS);
}
}
五, Lambda表达式
Lambda表达式相当于直接使用方法,不要对象
一个接口只有一个抽象方法才能使用Lambda表达式
public static void main(String[] args) {
/*print(new MyMath() {
@Override
public int sun(int x, int y) {
return x+y;
}
},5,3);*/
print((int x,int y) -> {
return x+y;
},1,3);
}
public static void print(MyMath m,int x,int y){
int num=m.sun(x,y);
System.out.println(num);
}
static interface MyMath{
int sun(int x,int y);
}
}
式
Lambda表达式相当于直接使用方法,不要对象
一个接口只有一个抽象方法才能使用Lambda表达式
public static void main(String[] args) {
/*print(new MyMath() {
@Override
public int sun(int x, int y) {
return x+y;
}
},5,3);*/
print((int x,int y) -> {
return x+y;
},1,3);
}
public static void print(MyMath m,int x,int y){
int num=m.sun(x,y);
System.out.println(num);
}
static interface MyMath{
int sun(int x,int y);
}
}