java多线程

多线程

程序:静态的代码;

进程:程序执行的过程,操作系统分配资源的单位

线程:CPU调度和执行的单位,独立执行的路径。互不影响,线程的执行由调度器安排,对同一资源进行操作时,需要并发控制

创建多线程,

方法1:定义一个类继承Thread,重写run()方法,run()方法里是程序执行体,

调用,创建一个该类的对象,并用 对象.start()方法开始

方法2:定义一个类实现Runnable借口,编写run()方法,创建一个该类的对象t1,再创建一个Thread类的匿名对象,把t1作为参数传进Thread类的构造方法。并调用start()方法。

方法3:实现callable接口,需要返回值类型;

重写call方法,需要抛出异常;

创建目标对象;几个线程就创几个对象

然后使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值

使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)

调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

线程池创建线程

1.创建Runnable或Callable的实现类对象;

2.创建执行服务:ExecutorService ser = Executor.newFixedThreadPool(1);

3.选择对应的方法

1)Runnable: ser.execute(Runnable的实现类对象);

2)Callable:提交执行:Future result1 = ser.submit(Callable的实现类对象);

获取结果:boolean r1 = result1.get();(Callable实现类才有返回值)

4.关闭服务:ser.shutdownNow();

多线程并发共享一个资源

多线程同时操作同一资源的情况下,线程不安全,数据紊乱。

模拟延时。Thread.sleep(200);

Thread.currentThread().getName();获取当前线程的名称

使用Callable接口的时候,几个线程就需要几个Callable实现类的对象,因为callable的call()方法是被Runnable的run()方法调用的。一个对象中的call()不能被几个线程同时调用,所以要创多个Callable实现类的对象

静态代理:

就是代替你去完成一些工作

静态代理模式总结:

真是对象和代理对象都要实现同一个接口,代理对象要代理真实角色。

好处:代理对象可以做很多真实对象做不了的事,真是对象专注做自己的事情。

new Tread(new Runnable(){

 	public void run(){

	}

} ).start();

Tread和Runnable都实现了Runnable接口,他们都有一个共同方法叫run()方法

Lamda表达式

任何接口,如果只包含唯一一个抽象方法,那么它就是函数式接口。

对于函数式接口,我们可以用lamda表达式来创建该接口的对象。

lambda表达式的前提必须是函数式接口

public class Lambda01{
	
	public static void main(String[] args){
		Ilike a = (str) -> System.out.println("I like " + str);
		a.talk("you");
	}
}

interface Ilike{
	abstract void talk(String str);
}

线程五大状态

1、创建状态

2、就绪状态

3、运行状态

4、阻塞状态

5、死亡状态

线程的停止

不推荐使用jdk提供的stop(),destory()等已废弃的方法。

推荐让线程自己停下来

建议使用一个标志位进行终止变量,当flag = false,则线程终止;

public class ThreadStop implements Runnable{
	private boolean flag = true;
	
	public void run(){
		int i = 0;
		while (flag){
			System.out.println("thread:" + i++);
		}
		System.out.println("stoping~~~");
	}
	
	public void stop(){
		this.flag = false;
	}
	
	public static void main(String[] args){
		ThreadStop t1 = new ThreadStop();
		new Thread(t1).start();
		for (int i = 0; i < 150; i++){
			System.out.println("main" + i);
			if (i == 90){
				t1.stop();
			}
		}
	}
}

线程休眠

模拟网络延时:放大问题的发生性

Thread.sleep(时间) ;当前线程阻塞的毫秒数

sleep存在InterruptedException(中断异常)

sleep时间到达后线程进入就绪状态;

sleep可以模拟网络延迟,倒计时等

每一个对象都有一个锁,sleep不会释放锁。

import java.util.Date;
import java.text.SimpleDateFormat;
public class CountDown{	

	public static void main(String[] args){
		// tenDown();
		// sysTime();
		
	}
	
	public static void sysTime(){		
		while (true){
			Date time = new Date(System.currentTimeMillis());
			System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time));		
			try{
				Thread.sleep(1000);
			} catch (InterruptedException e){
				e.printStackTrace();
			}
		}
	}
	
	public static void tenDown(){
		int num = 10;
		while (num >= 0){
			try{
				Thread.sleep(1000);
			} catch (InterruptedException e){
				e.printStackTrace();
			}
			
			System.out.println(num--);
		}
	}
}

现成礼让

Thread.yield();

礼让线程,让当前正在执行的线程暂停,但不阻塞。

将线程从运行状态转为就绪状态

让CPU重新调度,礼让不一定成功!看CPU心情

线程强制执行

Thread.join();

join合并线程,待此线程执行完成之后,再执行其他线程,其他线程阻塞

可以想象成插队

线程状态

新生状态,就绪状态,运行状态,阻塞状态,死亡;

监听线程状态,Thread.State state = Thread.getState()

Thread.State.TERMINATED 线程死亡

线程优先级

线程优先级越高,权重越高,越容易优先执行

Thread.getPriority()获取优先级

Thread.setPriority()设置优先级

优先级的范围是从1~10;最小是1,最大是10;寻常优先级为5;

守护线程

线程分为用户线程和守护线程

虚拟机必须确保用户线程执行完毕

虚拟机不用等待用户线程执行完毕

守护线程有后台记录操作日志、监控内存、垃圾回收等待

设置守护线程Thread.setDaemon(true);默认为false;

线程同步机制

多个线程调用同一资源,会引发并发问题,因为线程修改同一资源时,是把资源复制到自己的内存空间再进行出列的,就会导致不安全。这时候需要并发控制和死锁,保证线程同步执行。

同步方法:

synchronized 修饰的方法只能一个对象调用完之后,另一个对象才能调用。锁的是调用该方法的对象。默认锁的this

同步代码块:

synchronized(obj){

}

保证该对象只能被一个对象访问完之后才能被另一个对象访问。锁的时候要锁变化的量,也就是要访问的资源。

ArrayList线程是不安全的;CopyOnWriteArrayList是安全的;

死锁

多个线程需要等待其他线程占有资源才能运行,多个线程相互等待对方的资源,都停止执行的情形就是死锁

多个线程互相抱着对方需要的资源,然后形成僵持。

同时拥有两个以上对象的锁,就可能发生死锁问题。

产生死锁的四个必要条件:

1、互斥条件:一个资源只能被一个进程调用。

2、请求与保持条件:一个进程因请求资源而阻塞时,对方获得的资源保持不放。

3、不剥夺条件:进程已获得资源,在未使用完之前不能强行剥夺。

4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源状态。

只要破坏其中的任意一个条件,就可以避免死锁。

可重入锁lock

显式定义同步锁。同步锁实用Lock对象充当。每次只能有一个线程对Lock对象加锁,ReentrantLock类实现了Lock,比较常用,性能更高,扩展性好,提供更多的子类

手动开启和关闭,synchronized是隐式锁,出了作用域自动释放。

优先使用顺序Lock>同步代码块>同步方法

使用方法:

private final ReentrantLock lock = new ReentrantLock();
public void m(){
	lock.lock();//启动锁
	try{
		//保证线程安全的代码;
	}
	finally{
		lock.unlock();
		//如果同步代码有异常,要讲unlock()写入finally语句块。
	}
}

生产者消费者问题

生产者生产出来东西,告诉消费者消费,没有产品时,要通知消费者等待。

synchronized可以阻止并发更新同一个资源,实现了同步,但是不能实现不同线程之间的通信。

obj.wait()表示线程一直等待,知道其他线程通知,与sleep不同的是,会释放锁;

obj.wait(long timeout)指定等待的毫秒数。

obj.notify()唤醒一个正在等待的线程

obj.notifyAll()唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度。

都是Object类的方法,只能在同步方法和同步代码块中使用。否则会抛出IIIegalMonitorStateException异常(非法监控状态异常)

  1. 生产者——>数据缓冲池——>消费者

    利用缓冲区解决:管程法

  2. 信号灯法,标志位解决。

    用于数据单一的情况,即线程依次轮换的情况

线程池

ExecutorService ser = Executors.newFixedThreadPool(5);

Runnable接口用:

ser.execute(对象);

Callable接口用:

Future r = ser.submit(对象);

ser.shutdown();关闭线程池;

可以提前创建好几个线程,放入线程池中,使用的时候,直接获取,使用完放回池中,可以避免频繁创建销毁线程,实现重复利用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值