首页 > 试题广场 >

Java中线程如何实现,如何实现多线程,线程安全在Java中

[问答题]
请你谈谈Java中线程如何实现,以及如何实现多线程,线程安全在Java中是如何实现的,线程的工作区是哪里
Java多线程实现方式主要有四种:继承Thread类、实现Runnable接口、实现Callable接口通过FutureTask包装器来创建Thread线程、使用ExecutorService、Callable、Future实现有返回结果的多线程。 其中前两种方式线程执行完后都没有返回值,后两种是带返回值的。 线程不安全的原因:  在操作系统中,线程是不拥有资源的,进程是拥有资源的。而线程是由进程创建的,一个进程可以创建多个线程,这些线程共享着进程中的资源。所以,当线程一起并发运行时,同时对一个数据进行修改,就可能会造成数据的不一致性 线程安全的解决机制: 1. 加锁。 (1) 锁能使其保护的代码以串行的形式来访问,当给一个复合操作加锁后,能使其成为原子操作。一种错误的思想是只要对写数据的方法加锁,其实这是错的,对数据进行操作的所有方法都需加锁,不管是读还是写。 (2) 加锁时需要考虑性能问题,不能总是一味地给整个方法加锁synchronized就了事了,应该将方法中不影响共享状态且执行时间比较长的代码分离出去。 (3) 加锁的含义不仅仅局限于互斥,还包括可见性。为了确保所有线程都能看见最新值,读操作和写操作必须使用同样的锁对象。 2. 不共享状态 (1) 无状态对象: 无状态对象一定是线程安全的,因为不会影响到其他线程。 (2) 线程关闭: 仅在单线程环境下使用。 3. 不可变对象 可以使用final修饰的对象保证线程安全,由于final修饰的引用型变量(除String外)不可变是指引用不可变,但其指向的对象是可变的,所以此类必须安全发布,也即不能对外提供可以修改final对象的接口。
发表于 2019-01-05 11:32:10 回复(2)
java的多线程实现主要有4种,1)继承thread类,重写里面的run方法,通过start来开启线程。2)实现runable接口,实现里面的run方法。3)实现Callable接口。4)创建线程池,常用线程池有new***dthreadpool(用于短期异步任务),newsinglethredpool(单线程池,所有任务串行执行),newfixedthreadpool(创建固定大小的线程池),newscheduledthreadpool(创建无限容量的线程池),还可以根据自己需求创建设置参数的线程池。
线程安全:synchronized可以用来对加锁,实现线程安全,可以对代码块使用,可以对一般方法进行加锁,用于静态方法时,相当于对整个对象加锁。volatile用于修饰对象,每次读写操作都强制访问写入内存,保证每次都是最新数据来保证变量的安全。使用原子变量(java.util.concurrent.atomic)。使用重写锁reentractlock(公平锁(每次从队首取)和非公平锁(新来的线程有很大概率取到))。
线程的工作区在栈区。
发表于 2019-05-06 19:03:15 回复(0)
Java中实现线程有两种方式: 1、1.继承Thread类 public class Thread extends Object implements Runnable 定义Thread类的子类,并重写Thread类的run()方法,创建子类对象(即线程对象),调用线程对象的start()方法来启动该线程。 2.实现Runnable接口 public interface Runnable 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法同样是该线程的执行体。创建该Runnable实现类的实例,并将此实例作为Thread的target(即构造函数中的参数)来创建Thread对象(该Thread对象才是真正的线程对象,只是该Threa
发表于 2019-04-29 19:39:46 回复(0)
1、1.继承Thread类 public class Thread extends Object implements Runnable 定义Thread类的子类,并重写Thread类的run()方法,创建子类对象(即线程对象),调用线程对象的start()方法来启动该线程。 2.实现Runnable接口 public interface Runnable 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法同样是该线程的执行体。创建该Runnable实现类的实例,并将此实例作为Thread的target(即构造函数中的参数)来创建Thread对象(该Thread对象才是真正的线程对象,只是该Threa 3.使用Callable和Future 创建Callable接口的实现类,并实现call()方法,该方法有返回值;创建Callable实现类的实例,使用FutureTask来包装Callable对象,并且也封装了call()方法的返回值;使用FutureTask作为Thread类的target创建并启动线程;调用FutureTask对象的get()方法返回子线程执行结束后的返回值。
发表于 2019-04-29 19:07:08 回复(0)

 Java中实现线程有两种方式,一种是继承Thread类,另一种是实现Runnable接口。、继承Thread类 总我们应该要把线程执行的代码放到run方法中,启动线程使用start方法。如果使用run方法,则这个不叫线程的运行,而是叫普通方法的调用。 2、实现Runnable接口 使用继承Thread类和实现Runnable接口的关系和区别: Thread类本身也是实现了Runnable接口,因此也是实现了Runnable接口中的run方法。 当使用继承Thread类去实现线程时,我们需要重写run方法,因为Thread类中的run方法本身什么事情都不干。 当使用实现Runnable接口去实现线程时,我们需要重写run方法,然后使用new Thread(Runnable)这种方式来生成线程对象,这个时候线程对象中的run方法才会去执行我们自己实现的Runnable接口中的run方法。  Java多线程实现的方式有四种 1.继承Thread类,重写run方法 2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target 3.通过Callable和FutureTask创建线程 4.通过线程池创建线程
发表于 2019-04-25 23:03:19 回复(0)
实现线程的方式有三种:1.继承thread类2.实现runnable接口3.使用callable和future
编辑于 2019-04-25 18:58:50 回复(0)
Java中实现线程有三种方式: 1、1.继承Thread类 public class Thread extends Object implements Runnable 定义Thread类的子类,并重写Thread类的run()方法,创建子类对象(即线程对象),调用线程对象的start()方法来启动该线程。 2.实现Runnable接口 public interface Runnable 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法同样是该线程的执行体。创建该Runnable实现类的实例,并将此实例作为Thread的target(即构造函数中的参数)来创建Thread对象(该Thread对象才是真正的线程对象,只是该Threa 3.使用Callable和Future 创建Callable接口的实现类,并实现call()方法,该方法有返回值;创建Callable实现类的实例,使用FutureTask来包装Callable对象,并且也封装了call()方法的返回值;使用FutureTask作为Thread类的target创建并启动线程;调用FutureTask对象的get()方法返回子线程执行结束后的返回值。 如何实现多线程: 首先是继承Thread类并重写run()方法 package com.csu.multiThread; public class MultiThreadExtendsThread extends Thread{ String name; public MultiThreadExtendsThread(String name) { this.name = name; } public void run() { for(int i=0;i<5;i++) { System.out.println(name+"运行:"+i); } } public static void main(String[] args) { MultiThreadExtendsThread thread1 = new MultiThreadExtendsThread("A"); MultiThreadExtendsThread thread2 = new MultiThreadExtendsThread("B"); thread1.start(); thread2.start(); } } 二是实现Runnable接口,然后重写run()方法, package com.csu.multiThread; public class MultiThreadImplRunnable implements Runnable{ String name; public MultiThreadImplRunnable(String name) { this.name = name; } @Override public void run() { for(int i=0;i<5;i++) { System.out.println(name+"运行:"+i); } } public static void main(String[] args) { MultiThreadExtendsThread thread1 = new MultiThreadExtendsThread("A"); MultiThreadExtendsThread thread2 = new MultiThreadExtendsThread("B"); new Thread(thread1).start(); new Thread(thread2).start(); } } 线程安全的实现: 最基本的:synchronized关键字。这个方法是最常用的,它通过互斥的方式保证同步。我们知道java中有几个操作是可以保证原子性的,其中lock/unlock就是一对。虽然java没有提供这两个字节码的接口,但是我们可以通过monitorenter/monitorexit,而synchronized会在块的前后调用两个字节码指令。同时synchronize对于同一条线程来说是可重入的;其次它也是阻塞的。我们知道java线程是映射到操作系统上的,而且是混用的内核态线程和用户态线程(N:M),而将线程从阻塞/唤醒,需要将线程从用户态转换到内核态,这样会消耗太亮的资源,所以synchronize是一个重量级锁 另外一种和synchronize类似的方法:ReentrantLock。它们两个的区别:(1)synchronize是隐式的,只要块内的代码执行完,就会释放当前的锁;而后者需要显式的调用unlock()方法手动释放,所以经常搭配try/finally方法(忘记在finally中unlock是非常危险的) (2)后者可以选择等待中断——即在当前持有锁线程长期不释放锁的情况下,正在等待的线程可以选择放弃等待选择处理其他的事情。 (3) 后者可以选择公平锁(虽然默认是非公平的,因为公平锁的吞吐量很受影响)即先来后到,按申请的顺序获得锁。 (4)可以绑定多个条件 前面提到的两种方式都是通过互斥来达到同步的目的,这其实是悲观锁的一种。下面介绍的是乐观锁,基于冲突检测的并发策略,不需要将线程挂起,因此又被成为非阻塞同步。 典型:CAS(Compare And Swap),通过Unsafe类提供。有三个操作数,内存位置、旧的预期值、和新的值;当且仅当内存地址V符合预期值A时,执行将值更新为新的预期值B。 存在的问题:“ABA”情况,即原值为A,但在检测之前发生了改变,变成了B,同时也在检测时变回了A;即不能保证这个值没有被其他线程更改过。 接下来是无同步方案: 可重入代码(纯代码):是一种无同步方案,在执行的任何时候去中断,转而执行其他的代码;在重新返回代码后,不会出现任何的错误。可重入性->线程安全,充分不必要条件。即可重入性的代码都是线程安全的,但反之不一定。简单判断原则:一个方法的返回结果是可预测的,只要输入了相同的数据,就都能返回相同的结果。 线程本地存储:即利用ThreadLocal类;每个Thread类中都有一个变量ThreadLocalMap,默认是为null的。它将为每一个线程创立一个该变量的副本。这样线程之间就不存在数据征用的问题了。适用情况:(1)数据库的Connection连接 (2)WEB中的“一个请求对应一个服务器线程”,在知乎上看到一个回答,解释的蛮清晰的。(3)Spring中创建的默认模式是Singleton单例 (4)“生产者-消费者问题” ThreadLocal就是变量在不同线程上的副本,不同线程不共享,所以对变量改动时就不需要考虑线程间同步的问题了 ThreadLocal在web应用开发中是一种很常见的技巧,当web端采用无状态写法时(比如stateless session bean和spring默认的singleton),就可以考虑把一些变量放在ThreadLocal中 举个简单例子,以理解意思为主:你有两个方法A和B都要用到变量userId,又不想传来传去,一个很自然的想法就是把userId设为成员变量,但是在无状态时,这样做就很可能有问题,因为多个request在同时使用同一个instance,userId在不同request下值是不一样的,就会出现逻辑错误 但由于同一个request下一般都是处于同一个线程,如果放在ThreadLocal的话,这个变量就被各个方法共享了,而又不影响其他request,这种情况下,你可以简单把它理解为是一种没有副作用的成员变量(作者:卡斯帕尔) 线程栈线程的每个方法被执行的时候,都会同时创建一个帧(Frame)用于存储本地变量表、操作栈、动态链接、方法出入口等信息。每一个方法的调用至完成,就意味着一个帧在VM栈中的入栈至出栈的过程。如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果VM栈可以动态扩展(VM Spec中允许固定长度的VM栈),当扩展时无法申请到足够内存则抛出OutOfMemoryError异常。
发表于 2019-04-25 18:00:24 回复(0)
继承Thread类 实现runnable接口 应用程序可以使用Executor框架来创建线程池
发表于 2019-04-25 17:50:40 回复(0)
1.继承Thread类 2.实现Runnable接口 3.使用callable和futue 实现多线程的方法:1.继承Thread类,重写run方法 2.实现Runnable接口,重写run方法 线程安全的实现是由synchronized实现的
发表于 2019-04-25 17:20:47 回复(0)
Java中实现线程有三种方式: 1、1.继承Thread类 public class Thread extends Object implements Runnable 定义Thread类的子类,并重写Thread类的run()方法,创建子类对象(即线程对象),调用线程对象的start()方法来启动该线程。 2.实现Runnable接口 public interface Runnable 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法同样是该线程的执行体。创建该Runnable实现类的实例,并将此实例作为Thread的target(即构造函数中的参数)来创建Thread对象(该Thread对象才是真正的线程对象,只是该Threa 3.使用Callable和Future 创建Callable接口的实现类,并实现call()方法,该方法有返回值;创建Callable实现类的实例,使用FutureTask来包装Callable对象,并且也封装了call()方法的返回值;使用FutureTask作为Thread类的target创建并启动线程;调用FutureTask对象的get()方法返回子线程执行结束后的返回值。 如何实现多线程: 首先是继承Thread类并重写run()方法 package com.csu.multiThread; public class MultiThreadExtendsThread extends Thread{ String name; public MultiThreadExtendsThread(String name) { this.name = name; } public void run() { for(int i=0;i<5;i++) { System.out.println(name+"运行:"+i); } } public static void main(String[] args) { MultiThreadExtendsThread thread1 = new MultiThreadExtendsThread("A"); MultiThreadExtendsThread thread2 = new MultiThreadExtendsThread("B"); thread1.start(); thread2.start(); } } 二是实现Runnable接口,然后重写run()方法, package com.csu.multiThread; public class MultiThreadImplRunnable implements Runnable{ String name; public MultiThreadImplRunnable(String name) { this.name = name; } @Override public void run() { for(int i=0;i<5;i++) { System.out.println(name+"运行:"+i); } } public static void main(String[] args) { MultiThreadExtendsThread thread1 = new MultiThreadExtendsThread("A"); MultiThreadExtendsThread thread2 = new MultiThreadExtendsThread("B"); new Thread(thread1).start(); new Thread(thread2).start(); } } 线程安全的实现: 最基本的:synchronized关键字。这个方法是最常用的,它通过互斥的方式保证同步。我们知道java中有几个操作是可以保证原子性的,其中lock/unlock就是一对。虽然java没有提供这两个字节码的接口,但是我们可以通过monitorenter/monitorexit,而synchronized会在块的前后调用两个字节码指令。同时synchronize对于同一条线程来说是可重入的;其次它也是阻塞的。我们知道java线程是映射到操作系统上的,而且是混用的内核态线程和用户态线程(N:M),而将线程从阻塞/唤醒,需要将线程从用户态转换到内核态,这样会消耗太亮的资源,所以synchronize是一个重量级锁 另外一种和synchronize类似的方法:ReentrantLock。它们两个的区别:(1)synchronize是隐式的,只要块内的代码执行完,就会释放当前的锁;而后者需要显式的调用unlock()方法手动释放,所以经常搭配try/finally方法(忘记在finally中unlock是非常危险的) (2)后者可以选择等待中断——即在当前持有锁线程长期不释放锁的情况下,正在等待的线程可以选择放弃等待选择处理其他的事情。 (3) 后者可以选择公平锁(虽然默认是非公平的,因为公平锁的吞吐量很受影响)即先来后到,按申请的顺序获得锁。 (4)可以绑定多个条件 前面提到的两种方式都是通过互斥来达到同步的目的,这其实是悲观锁的一种。下面介绍的是乐观锁,基于冲突检测的并发策略,不需要将线程挂起,因此又被成为非阻塞同步。 典型:CAS(Compare And Swap),通过Unsafe类提供。有三个操作数,内存位置、旧的预期值、和新的值;当且仅当内存地址V符合预期值A时,执行将值更新为新的预期值B。 存在的问题:“ABA”情况,即原值为A,但在检测之前发生了改变,变成了B,同时也在检测时变回了A;即不能保证这个值没有被其他线程更改过。 接下来是无同步方案: 可重入代码(纯代码):是一种无同步方案,在执行的任何时候去中断,转而执行其他的代码;在重新返回代码后,不会出现任何的错误。可重入性->线程安全,充分不必要条件。即可重入性的代码都是线程安全的,但反之不一定。简单判断原则:一个方法的返回结果是可预测的,只要输入了相同的数据,就都能返回相同的结果。 线程本地存储:即利用ThreadLocal类;每个Thread类中都有一个变量ThreadLocalMap,默认是为null的。它将为每一个线程创立一个该变量的副本。这样线程之间就不存在数据征用的问题了。适用情况:(1)数据库的Connection连接 (2)WEB中的“一个请求对应一个服务器线程”,在知乎上看到一个回答,解释的蛮清晰的。(3)Spring中创建的默认模式是Singleton单例 (4)“生产者-消费者问题” ThreadLocal就是变量在不同线程上的副本,不同线程不共享,所以对变量改动时就不需要考虑线程间同步的问题了 ThreadLocal在web应用开发中是一种很常见的技巧,当web端采用无状态写法时(比如stateless session bean和spring默认的singleton),就可以考虑把一些变量放在ThreadLocal中 举个简单例子,以理解意思为主:你有两个方法A和B都要用到变量userId,又不想传来传去,一个很自然的想法就是把userId设为成员变量,但是在无状态时,这样做就很可能有问题,因为多个request在同时使用同一个instance,userId在不同request下值是不一样的,就会出现逻辑错误 但由于同一个request下一般都是处于同一个线程,如果放在ThreadLocal的话,这个变量就被各个方法共享了,而又不影响其他request,这种情况下,你可以简单把它理解为是一种没有副作用的成员变量(作者:卡斯帕尔) 线程栈线程的每个方法被执行的时候,都会同时创建一个帧(Frame)用于存储本地变量表、操作栈、动态链接、方法出入口等信息。每一个方法的调用至完成,就意味着一个帧在VM栈中的入栈至出栈的过程。如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果VM栈可以动态扩展(VM Spec中允许固定长度的VM栈),当扩展时无法申请到足够内存则抛出OutOfMemoryError异常。
发表于 2019-04-25 14:30:55 回复(0)
Ly~头像 Ly~
线程的实现方式 从JDK的文档上看,在Java中实现线程有两种方式,一种是继承Thread类,另一种是实现Runnable接口。、继承Thread类 总我们应该要把线程执行的代码放到run方法中,启动线程使用start方法。如果使用run方法,则这个不叫线程的运行,而是叫普通方法的调用。 2、实现Runnable接口 使用继承Thread类和实现Runnable接口的关系和区别: Thread类本身也是实现了Runnable接口,因此也是实现了Runnable接口中的run方法。 当使用继承Thread类去实现线程时,我们需要重写run方法,因为Thread类中的run方法本身什么事情都不干。 当使用实现Runnable接口去实现线程时,我们需要重写run方法,然后使用new Thread(Runnable)这种方式来生成线程对象,这个时候线程对象中的run方法才会去执行我们自己实现的Runnable接口中的run方法。 Java多线程实现的方式有四种 1.继承Thread类,重写run方法 2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target 3.通过Callable和FutureTask创建线程 4.通过线程池创建线程
发表于 2019-04-25 11:50:45 回复(0)