怎么实现多线程,如何学习Java多线程?

怎么实现多线程,如何学习Java多线程?

JAVA中的多线程使用十分广泛,很多的JAVA框架都使用到了多线程,比如spring,mybatis,druid等!

多线程有什么好处呢?比如说web服务器的多连接,异步调用,并行操作,避免持续阻塞等等!
怎么实现多线程,如何学习Java多线程?

1、解释实现多线程的几种方法?

一Java线程可以实现Runnable接口或者继承Thread类来实现,当你打算多重继承时,优先选择实现Runnable。

2、Thread.start()与Thread.run()有什么区别?

Thread.start()方法(native)启动线程,使之进入就绪状态,当cpu分配时间该线程时,由JVM调度执行run()方法。

3、为什么需要run()和start()方法,我们可以只用run()方法来完成任务吗?

我们需要run()&start()这两个方法是因为JVM创建一个单独的线程不同于普通方法的调用, 所以这项工作由线程的start方法来完成,start由本地方法实现,需要显示地被调用,使用这俩个方法的另外一个好处是任何一个对象都可以作为线程运 行,只要实现了Runnable接口,这就避免因继承了Thread类而造成的Java的多继承问题。

4、什么是ThreadLocal类,怎么使用它?

ThreadLocal是一个线程级别的局部变量,并非“本地线程”。ThreadLocal为每个使用该变量的线程提供了一个独立的变量副本,每个线程修改副本时不影响其它线程对象的副本(译者注)。

下面是线程局部变量(ThreadLocal variables)的关键点:

一个线程局部变量(ThreadLocal variables)为每个线程方便地提供了一个单独的变量。

ThreadLocal实例通常作为静态的私有的(private static)字段出现在一个类中,这个类用来关联一个线程。

当多个线程访问ThreadLocal实例时,每个线程维护ThreadLocal提供的独立的变量副本。

常用的使用可在DAO模式中见到,当DAO类作为一个单例类时,数据库链接(connection)被每一个线程独立的维护,互不影响。(基于线程的单例)

5、什么时候抛出InvalidMonitorStateException异常,为什么?

调用wait()/notify()/notifyAll()中的任何一个方法时,如果当前线程没有获得该对象 的锁,那么就会抛出IllegalMonitorStateException的异常(也就是说程序在没有执行对象的任何同步块或者同步方法时,仍然尝试 调用wait()/notify()/notifyAll()时)。由于该异常是RuntimeExcpetion的子类,所以该异常不一定要捕获(尽管 你可以捕获只要你愿意).作为RuntimeException,此类异常不会在wait(),notify(),notifyAll()的方法签名提 及。

6、Sleep()、suspend()和wait()之间有什么区别?

Thread.sleep()使当前线程在指定的时间处于“非运行”(Not Runnable)状态。线程一直持有对象的监视器。比如一个线程当前在一个同步块或同步方法中,其它线程不能进入该块或方法中。如果另一线程调用了 interrupt()方法,它将唤醒那个“睡眠的”线程。

注意:sleep()是一个静态方法。这意味着只对当前线程有效,一个常见的错误是调用t.sleep(),(这里的t是一个不同于当前线程的线 程)。即便是执行t.sleep(),也是当前线程进入睡眠,而不是t线程。t.suspend()是过时的方法,使用suspend()导致线程进入停 滞状态,该线程会一直持有对象的监视器,suspend()容易引起死锁问题。

object.wait()使当前线程出于“不可运行”状态,和sleep()不同的是wait是object的方法而不是thread。调用 object.wait()时,线程先要获取这个对象的对象锁,当前线程必须在锁对象保持同步,把当前线程添加到等待队列中,随后另一线程可以同步同一个 对象锁来调用object.notify(),这样将唤醒原来等待中的线程,然后释放该锁。基本上wait()/notify()与sleep() /interrupt()类似,只是前者需要获取对象锁。

7、在静态方法上使用同步时会发生什么事?

同步静态方法时会获取该类的“Class”对象,所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。

8、当一个同步方法已经执行,线程能够调用对象上的非同步实例方法吗?

可以,一个非同步方法总是可以被调用而不会有任何问题。实际上,Java没有为非同步方法做任何检查,锁对象仅 仅在同步方法或者同步代码块中检查。如果一个方法没有声明为同步,即使你在使用共享数据Java照样会调用,而不会做检查是否安全,所以在这种情况下要特 别小心。一个方法是否声明为同步取决于临界区访问(critial section access),如果方法不访问临界区(共享资源或者数据结构)就没必要声明为同步的。

9、 在一个对象上两个线程可以调用两个不同的同步实例方法吗?

不能,因为一个对象已经同步了实例方法,线程获取了对象的对象锁。所以只有执行完该方法释放对象锁后才能执行其 它同步方法。看下面代码示例非常清晰:Common 类 有synchronizedMethod1()和synchronizedMethod2()方法,MyThread调用这两个方法。

10、 什么是死锁

死锁就是两个或两个以上的线程被无限的阻塞,线程之间相互等待所需资源。这种情况可能发生在当两个线程尝试获取其它资源的锁,而每个线程又陷入无限等待其它资源锁的释放,除非一个用户进程被终止。就JavaAPI而言,线程死锁可能发生在一下情况。

●当两个线程相互调用Thread.join()

●当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。

11、什么是线程饿死,什么是活锁?

线程饿死和活锁虽然不想是死锁一样的常见问题,但是对于并发编程的设计者来说就像一次邂逅一样。

当所有线程阻塞,或者由于需要的资源无效而不能处理,不存在非阻塞线程使资源可用。JavaAPI中线程活锁可能发生在以下情形:

●当所有线程在程序中执行Object.wait(0),参数为0的wait方法。程序将发生活锁直到在相应的对象上有线程调用Object.notify()或者Object.notifyAll()。

●当所有线程卡在无限循环中。

如何创建并运行java线程?

在java语言中,线程的类名是Thread,有两种方式创建线程:直接创建Thread对象或者创建线程池,由线程池来管理线程。

直接创建Thread对象

我们可以直接用默认构造函数创建一个线程

Thread thread = new Thread();

然后调用它的start方法来启动线程:

thread.start();

此时线程并不会执行业务代码,很快就会执行完毕。

要想让线程运行目标代码,有多种方式:

1、将目标代码编写在一个实现了Runnable接口的类的run方法中,创建线程时,用这个类的实例作为构造函数参数;

怎么实现多线程,如何学习Java多线程?

怎么实现多线程,如何学习Java多线程?

或者直接创建一个Runnable接口的匿名子类,用这个

怎么实现多线程,如何学习Java多线程?

2、创建一个Thread类的子类,在子类重写run方法,在run方法中实现业务:

怎么实现多线程,如何学习Java多线程?

然后再创建一个Thread子类实例,并调用start方法执行业务代码怎么实现多线程,如何学习Java多线程?

或者创建一个Thread的匿名子类:

怎么实现多线程,如何学习Java多线程?

使用线程池

Executors类提供了四类创建线程池的静态方法,它们都会返回ExecutorService接口的实例,这些实例就是线程池,只需要调用它的submit方法,将要执行的任务提交给线程池,线程池就会按照自己的策略来执行任务。而这个任务,就是实现了Runnable接口的对象。

怎么实现多线程,如何学习Java多线程?

以上就是java中创建线程和执行线程的方法,希望可以帮助到题主,欢迎大家批评指正。

如果喜欢我的回答,请点赞,关注,谢谢!

如何理解应用Java多线程与并发编程?

你好,很高兴回答你的问题!下面是Java多线程与并发编程详解整合,希望对你有所帮助!

一、多线程三大特性

多线程有三大特性:原子性、可见性、有序性。

原子性

(跟数据库的事务特性中的原子性类似,数据库的原子性体现是dml语句执行后需要进行提交):
理解:即一个操作或多个操作,要么全部执行并且执行的过程中不会被任何因素打断,要么都不执行。
一个很经典的例子就是银行账户转账问题:
比如从账户A向账户B转5000元,那么必然包括2个操作:从账户A减去5000元,往账户B加上5000元。这2个操作必须要具备原子性才能保证不出现一些意外的问题。
我们操作数据也是如此,比如i = i+1;其中就包括,读取i的值,计算i,写入i。这行代码在Java中是不具备原子性的,则多线程运行肯定会出问题,所以也需要我们使用同步synchronized和lock锁这些东西来确保这个特性了。
原子性其实就是保证数据一致、线程安全一部分,

可见性:

可见性是与java内存模型息息相关的。
当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
若两个线程在不同的cpu,那么线程1改变了i的值还没刷新到主存,线程2又使用了i,那么这个i值肯定还是之前的,线程1对变量的修改线程2没有看到,这就是可见性问题。

有序性:

理解:程序执行的顺序按照代码的先后顺序执行。
一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。

二、Java内存模型

jvm的内存结构为:堆、栈、方法区,不同于java的内存模型,Java的内存模型是关于多线程相关的。

理解:共享内存模型指的是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中(局部变量不会存储在),每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编辑器优化。

总结:什么是Java内存模型:java内存模型简称jmm,定义了一个线程对另一个线程可见。共享变量存放在主内存中,每个线程都有自己的本地内存,当多个线程同时访问一个数据的时候,可能本地内存没有及时刷新到主内存,所以就会发生线程安全问题。

三、Volatile关键字

Volatile关键字的作用:变量在多个线程之间可见。

Volatile关键字是非原子性的,不能保证数据的原子性,只是能够把解决立马刷新到主内存中,不能解决并发问题。

如果想要保证数据的原子性,解决并发问题,需要使用并发包里的AutomicInteger原子类。

volatile与synchronized区别:
仅靠volatile不能保证线程的安全性(原子性)。

  1. 1.volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法。
  2. 2.volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。

四、TreadLocal

1.什么是ThreadLocal?

ThreadLocal提高一个线程的局部变量,访问某个线程拥有自己局部变量。

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程对应的副本。

ThreadLocal接口方法有4个:

  1. void set(Object value)设置当前线程的线程局部变量的值;
  2. public Object get()该方法返回当前线程所对应的线程局部变量;
  3. public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存的回收速度;
  4. protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

2.ThreadLocal底层实现原理:

ThreadLocal通过Thread.currentThread();获取当前线程

操作map集合:ThreadLocalMap

void set(Object value)就是Map.put(“当前线程”,值);

public Object get()就是获取ThreadLocalMap然后操作后返回。

五、线程池

1.为什么要使用线程池?

因为要通过线程池来管理线程,启动或者停止一个线程非常耗费资源,所以将线程交给线程池来管理能够节约内存。
一般在企业开发当中我们都使用线程池,通过spring去整合线程池,异步注解。

2.什么是线程池?

线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。线程池中线程的数量通常完全取决于可用内存数量和应用程序的需求。然而,增加可用线程数量是可能的。线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中并等待下一次分配任务。

3.线程池作用:

基于以下几个原因,在多线程应用程序中使用线程池是必须的:

  1. 1.线程池改进了一个应用程序的相应时间。由于线程池中的线程已经准备好且等待被分配任务,应用程序可以直接拿来使用而不用新建一个线程。
  2. 2.线程池节省了CLR为每个短生命周期任务创建一个完整的线程开销并可以在任务完成后回收资源。
  3. 3.线程池根据当前在系统中运行的进程来优化线程时间片。
  4. 4.线程池允许我们开启多个任务而不用为每个线程设置属性。
  5. 5.线程池允许我们为正在执行任务的程序参数传递一个包含状态信息的对象引用。
  6. 6.线程池可以用来解决处理一个特定请求最大线程数量限制问题。

4.线程池四种创建方式:

java通过Executors(jdk1.5的并发包)提供四种线程池,分别为:

  1. 1.newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  2. 2.newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  3. 3.newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行
  4. 4.newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。

总结:newCachedThreadPool 创建的线程,线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。newFixedThreadPool 每次执行传入参数大小个线程,其他线程在等待(企业中用的不多)。newScheduledThreadPool 使用schedule方法创建单位时间的延迟线程池。

怎么实现多线程,如何学习Java多线程?

怎么实现多线程,如何学习Java多线程?

怎么实现多线程,如何学习Java多线程?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 xxx@163.com 举报,一经查实,本站将立刻删除。

发表评论

登录后才能评论