java基础四(线程)
1.概念
2.线程的实现
3.lamda简化线程
4.线程状态
4.1状态转换
5.并发和同步
5.1锁机制、同步块
6.通信
一、概念
1、进程:一个进程对应一个程序。一个进程可以有多个线程。
2、线程:是一个独立的执行路径。是程序中一个执行流。以CPU为主体。java中的线程是由虚拟CPU、代码、数据组成。代码和数据是相互独立的。代码和数据构成线程体,线程的行为由线程体决定。虚拟CPU是在创建线程时自动封装进Thread类的实例中。
3、在程序运行时,即使自己没有创建线程,后台也会有多个线程。
4、main()为主线程。为程序的入口。
5、线程的调度由调度器安排,先后顺序是不能认为干预。
6、线程的优先级:priority设置。级别必须在1~10之间。并且优先级不代表执行的顺序
7、守护线程(daemon):JVM不需要等待守护线程执行完,setDaemon(true)转换为守护程序
8、死锁:多个线程占有一些共享资源,相互不释放。相互等待。造成死锁。
9、线程的调度:在单个CPU上以某种顺序运行多个线程。
二、线程创建的三个方法
1、继承Thread接口,重写run()方法
基本步骤:继承接口,重写run方法,创建线程对象,使用start()开启线程。
注意:
执行线程必须调用start()方法才可以加入到调度器中。
加入调度器不一定立即执行,需要等待调度器的分配执行
如果直接调用run()方法。不是开启多线程。而是普通方法的调用。
例子:使用线程执行1加到100的程序
public class text extends Thread{//继承Thread @Override public void run() { //重写run()方法 super.run(); int num = 1; for(int i = 2; i <= 100; i++) { num += i; } System.out.println("1+2+3+.....+100="+num); } public static void main(String[] args){ text tt = new text();//创建线程对象 tt.start(); //开启线程 } }
2、实现Runnable
需要创建线程对象,目标对象,再启动线程。
优点:避免单继承的局限性。使用接口。方便共享。
例子:使用Runnable计算1加到100
public class text implements Runnable{//实现Runnable @Override public void run() { //重写run()方法 int num = 1; for(int i = 2; i <= 100; i++) { num += i; } System.out.println("1+2+3+.....+100="+num); } public static void main(String[] args){ text tt = new text();//创建目标对象 Thread th = new Thread(tt);//创建线程对象。 th.start();//开启线程 //如果是使用一次。可以使用匿名写法。 //new Thread(tt).start(); } }
3、实现Callable
需要的步骤:创建目标对象、创建执行服务、提交执行、获取结果、关闭服务
一般出去工作一段时间后,才可能使用到。
public class text implements Callable{//实现Callable @Override public Object call() throws Exception {//重写call() int num = 1; for(int i = 2; i <= 100; i++) { num += i; } System.out.println("1+2+3+.....+100="+num); return true; } public static void main(String[] args) throws InterruptedException, ExecutionException{ //(1)创建目标对象 text tt = new text(); //(2)创建执行服务 ExecutorService es = Executors.newFixedThreadPool(1); //(3)执行服务 Future<Boolean> submit = es.submit(tt); //(4)获取结果 boolean object = submit.get(); //(5)关闭服务 es.shutdownNow(); } }
三、lamda表达式
为了避免匿名内部类过多,属于函数式编程的概念。
1、只用一次(并且没有参数和返回值)
后续补充
四、线程状态

导致线程就绪状态转换的方法:
调用start()、阻塞解除、调用yield()、JVM本身的线程切换。
导致阻塞状态转换的方法:
调用sleep()、wait()、Join()、IO中的read()、write()
几个方法:
currentThread:返回当前线程。 isAlive:判断当前线程是否活着。
suspend:暂停线程。如果想要恢复这个线程,一定要其他线程调用resume恢复当前线程。
转换方法
1、线程停止(使用boolean)
JDK提供的stop()、destroy() 已经被jdk废弃的了。所以不用这个
可以设置一个boolean值。进行线程的开启和停止。
2、线程暂停(sleep)
可以使用sleep(时间),进行睡眠时间的设置,达到一个线程暂停的效果。
时间是毫秒数。而且,sleep不会释放锁的。
3、线程礼让(yield)
礼让线程是让当前的线程暂停。不是阻塞,将当前线程从运行状态转入就绪状态。
让cpu重新调度。
4、线程插队(Join)
插队,就是阻塞当前正在运行的线程。等待插队线程执行完了。在执行其他线程。
是一个阻塞状态。
5、interrupt
如果一格线程正在处于阻塞时,使用interrupt方法将使该线程中断的状态被清除。并且该线程会接收到InterruptExcaption异常。
五、并发和同步
并发:是同一个对象多个线程同时操作。
同步:是一个等待机制。多个需要访问同一个对象的线程进入等待池形成队列。需要等待前面的线程使用完毕后,下一个线程再使用这个资源。
临界区:一个程序中单独的、并发的线程对同一个对象进行访问的代码段,也可以是语句块。
对象锁:将每个由synchronize语句指定的对象设置成一个锁。是一种独特的排它锁。如果一个线程获得对象的锁后,便拥有了对象的操作权,其他任何线程不能对该对象进行任何操作。
线程的安全:需要形成队列、并且使用锁机制。
1、获得对象的排它锁后,独占资源,其他线程必须等待。
2、多线程竞争下,加锁、释放锁导致比较多的上下文切换和调度延迟。引起性能问题
3、如果优先级高的线程等待优先级低的线程,会优先导致,引起性能问题
锁机制、synchronize块
每一个对象都对应一把锁,每一个synchronize方法必须获得调用该方法的对象锁才可以执行。
否则,该线程阻塞。方法一旦执行,就会独占该锁。直到方法返回时,才将锁释放。被阻塞的
线程才能获取该锁,重新进入可执行状态。
synchronize同步块:synchronize(obj){ },obj称为同步监视器
任何对象都可以作为同步监视器。
同步方法无需指定同步监视器,因为同步方法的同步监视器是对象本身。
在容器CopyOnWriteArrayList<类型>中已经有集成的锁机制了。
六、通信

在图中。仓库在有货品的时候,需要通知消费者。然后没有货的时候,通知生产者
这样一个消息的传递(通信)
因为synchronize同步块,可以阻止并发更新同一个共享资源,实现同步。
但是,不能实现不同线程之间的通信问题。
解决通信问题:
1、只能在同步方法或者同步代码中使用一下方法。可以解决通信问题。
wait():线程一直等待,直到线程通知。与sleep()不同。会释放锁。
notifiy(): 唤醒一个处于等待状态的线程
ontifyAll():唤醒同一个对象上所有调用wait()方法的线程。优先级高的优先调度
2、管程
并发协作模型“生产者/消费者模式”,就是管程。
在生产者和消费者之间设置一个缓冲区。容器存在数据,消费者可以消费,
数据为空,消费者等待,生产者可以进行生产。数据满了,生产者等待
3、进行boolean的设置。像信号灯一样
就是使用布尔值,进行开关的设置,注意:不要忘记了等待之后,需要再切换回来。
4、定时调度
使用Timer、Timetask实现定时启动某个线程。

浙公网安备 33010602011771号