JAVA多线程编程(详细操作例子)(低分分享)

JAVA多线程编程(详细操作例子)(低分分享)
JAVA多线程编程(详细操作例子)(低分分享)

多线程

一、理解多线程

多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。

具体到java内存模型,由于Java被设计为跨平台的语言,在内存管理上,显然也要有一个统一的模型。系统存在一个主内存(Main Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。

多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的。多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,将会带来线程调度,同步等问题。

二、在Java中实现多线程

我们不妨设想,为了创建一个新的线程,我们需要做些什么?很显然,我们必须指明这个线程所要执行的代码,而这就是在Java中实现多线程我们所需要做的一切!

作为一个完全面向对象的语言,Java提供了类https://www.360docs.net/doc/e35588598.html,ng.Thread 来方便多线程编程,这个类提供了大量的方法来方便我们控制自己的各个线程。

那么如何提供给Java 我们要线程执行的代码呢?让我们来看一看Thread 类。Thread 类最重要的方法是run(),它为Thread 类的方法start()所调用,提供我们的线程所要执行的代码。为了指定我们自己的代码,只需要覆盖它!

方法一:继承Thread 类,重写方法run(),我们在创建Thread 类的子类中重写run (),加入线程所要执行的代码即可。下面是一个例子:

public class TwoThread extends Thread {

public void run() {

for ( int i = 0; i < 10; i++ ) {

System.out.println("New thread");

}

}

public static void main(String[] args) {

TwoThread tt = new TwoThread();

tt.start();

for ( int i = 0; i < 10; i++ ) {

System.out.println("Main thread");

}

}

}

这种方法简单明了,符合大家的习惯,但是,它也有一个很大的缺点,那就是如果我们的类已经从一个类继承,则无法再继承Thread 类。

方法二:实现Runnable 接口

Runnable 接口只有一个方法run(),我们声明自己的类实现Runnable 接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分的任务。但是Runnable 接口并没有任何对线程的支持,我们还必须创建Thread 类的实例,这一点通过Thread 类的构造函数public Thread(Runnable target);来实现。下面是一个例子:

public class MyThread implements Runnable {

int count=1, number;

public MyThread(int num) {

number = num;

System.out.println("创建线程" + number);

}

public void run() {//实现了接口的run()方法

while(true) {

System.out.println("线程" + number + ":计数" + count);

if(++count== 6) return;

}

}

public static void main(String args[]) {

for(int i = 0; i < 5; i++)

new Thread(new MyThread(i+1)).start();

}

}

使用Runnable 接口来实现多线程使得我们能够在一个类中包容所有的代码,有利于封装下面让我们一起来研究一下多线程使用中的一些问题。

三、线程的四种状态

1、新状态:线程已被创建但尚未执行(start()尚未被调用)。

2、可执行状态:线程可以执行,虽然不一定正在执行。CPU 时间随时可能被分配给该

线程,从而使得它执行。

3、阻塞状态:线程不会被分配CPU 时间,无法执行;可能阻塞于I/O,或者阻塞于同步锁。

4、死亡状态:正常情况下run()返回使得线程死亡。调用stop()或destroy()亦有同样效果,但是不被推荐,前者会产生异常,后者是强制终止,不会释放锁。

四、线程的优先级

线程的优先级代表该线程的重要程度,当有多个线程同时处于可执行状态并等待获得CPU 时间时,线程调度系统根据各个线程的优先级来决定给谁分配CPU 时间,优先级高的线程有更大的机会获得CPU 时间,优先级低的线程也不是没有机会,只是机会要小一些罢了。

你可以调用Thread 类的方法getPriority()和setPriority()来存取线程的优先级,线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间,缺省是5(NORM_PRIORITY)。

状态图1

状态图2

五、线程的同步

由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。

我们只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized 方法和synchronized 块。

1. synchronized 方法:通过在方法声明中加入synchronized关键字来声明synchronized 方法。synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为synchronized)。

在Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为synchronized,以控制其对类的静态成员变量的访问。

synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法run()声明为synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何synchronized方法的调用都永远不会成功。

2. synchronized 块:通过synchronized关键字来声明synchronized 块。语法如下:

synchronized(syncObject) {

//允许访问控制的代码

}

synchronized 块是这样一个代码块,其中的代码必须获得对象syncObject 的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

六、线程的阻塞

为了解决对共享存储区的访问冲突,Java 引入了同步机制,现在让我们来考察多个线程对共享资源的访问,显然同步机制已经不够了,因为在任意时刻所要求的资源不一定已经准备好了被访问,反过来,同一时刻准备好了的资源也可能不止一个。为了解决这种情况下的访问控制问题,Java 引入了对阻塞机制的支持。

阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。Java 提供了大量方法来支持阻塞,下面让对它们逐一分析。

1. sleep()方法:sleep()允许指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。

典型地,sleep()被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。

2. suspend()和resume()方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume()被调用,才能使得线程重新进入可执行状态。典型地,suspend()和resume()被用在等待另一个线程产生的结果的情形:

测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用resume()使其恢复。

3. yield()方法:yield()使得线程放弃当前分得的CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得CPU 时间。调用yield()的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。

4. wait()和notify()方法:两个方法配套使用,wait()使得线程进入阻塞状态,它有两种形式,一种允许指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的notify()被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的notify()被调用。

2和4区别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法则相反。上述的核心区别导致了一系列的细节上的区别。

首先,前面叙述的所有方法都隶属于Thread 类,但是这一对却直接隶属于Object 类,也就是说,所有对象都拥有这一对方法。因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的wait()方法导致线程阻塞,并且该对象上的锁被释放。而调用任意对象的notify()方法则导致因调用该对象的wait()方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException 异常。

wait()和notify()方法的上述特性决定了它们经常和synchronized 方法或块一起使用,将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized 方法或块提供了类似于操作系统原语的功能,它们的结合用于解决各种复杂的线程间通信问题。

关于wait()和notify()方法最后再说明两点:

第一:调用notify()方法导致解除阻塞的线程是从因调用该对象的wait()方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。

第二:除了notify(),还有一个方法notifyAll()也可起到类似作用,唯一的区别在于,调用notifyAll()方法将把因调用该对象的wait()方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。

谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend()方法和不指定超时

期限的wait()方法的调用都可能产生死锁。遗憾的是,Java 并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。

以上我们对Java 中实现线程阻塞的各种方法作了一番分析,我们重点分析了wait()和notify()方法,因为它们的功能最强大,使用也最灵活,但是这也导致了它们的效率较低,较容易出错。实际使用中我们应该灵活使用各种方法,以便更好地达到我们的目的。

还有一个方法比较有用,Thread的join()方法可用于让当前线程阻塞,以等待特定线程的消亡。

package cn.test.first.thread;

import https://www.360docs.net/doc/e35588598.html,ng.Runnable;

import https://www.360docs.net/doc/e35588598.html,ng.Thread;

public class DemoThread implements Runnable {

public DemoThread() {

TestThread testthread1 = new TestThread(this, "1");

TestThread testthread2 = new TestThread(this, "2");

testthread2.start();

testthread1.start();

}

public static void main(String[] args) {

DemoThread demoThread1 = new DemoThread();

}

public void run() {

TestThread t = (TestThread) Thread.currentThread();

try {

if (!t.getName().equalsIgnoreCase("1")) {

synchronized (this) {

wait();

}

}

while (true) {

System.out.println("@time in thread" + t.getName() + "=" +

t.increaseTime());

if (t.getTime() % 2 == 0) {

synchronized (this) {

System.out.println("****************************************");

notify();

if (t.getTime() == 10) {

break;

}

wait();

}

}

}

}

catch (Exception e) {

e.printStackTrace();

}

}

}

class TestThread extends Thread {

private int time = 0;

public TestThread(Runnable r, String name) { super(r, name);

}

public int getTime() {

return time;

}

public int increaseTime() {

return ++time;

}

}

运行结

@time in thread1=1

@time in thread1=2

****************************************

@time in thread2=1

@time in thread2=2

****************************************

@time in thread1=3

@time in thread1=4

****************************************

@time in thread2=3

@time in thread2=4

****************************************

……

@time in thread1=9

@time in thread1=10

****************************************

@time in thread2=9

@time in thread2=10

****************************************

七、守护线程

守护线程是一类特殊的线程,它和普通线程的区别在于它并不是应用程序的核心部分,当一个应用程序的所有非守护线程终止运行时,即使仍然有守护线程在运行,应用程序也将终止,反之,只要有一个非守护线程在运行,应用程序就不会终止。守护线程一般被用于在后台为其它线程提供服务。调用方法isDaemon()来判断一个线程是否是守护线程,也可以调用方法setDaemon()将一个线程设为守护线程。

八、一个应用:线程池

1.背景

诸如Web 服务器、数据库服务器、文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务。服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的。

2.解决方法

构建服务器应用程序的一个过于简单的模型应该是:每当一个请求到达就创建一个新线程,然后在新线程中为请求服务。

那么这种方法的严重不足就很明显。每个请求对应一个线程(thread-per-request)方法的不足之一是:为每个请求创建一个新线程的开销很大;在一个JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻处理的请求数目。

我们可以实现一个线程池类,其中客户机类等待一个可用线程、将任务传递给该线程以便执行、然后在任务完成时将线程归还给池,一般一个简单线程池至少包含下列组成部分:

线程池管理器(ThreadPoolManager):用于创建并管理线程池

工作线程(WorkThread):线程池中线程

任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。

任务队列:用于存放没有处理的任务。提供一种缓冲机制。

public class WorkQueue {

private final int nThreads;

private final PoolWorker[] threads;

private final LinkedList queue;

public WorkQueue(int nThreads) {

this.nThreads = nThreads;

queue = new LinkedList();

threads = new PoolWorker[nThreads];

for (int i = 0; i < nThreads; i++) {

threads[i] = new PoolWorker();

threads[i].start();

}

}

public void execute(Runnable r) {

synchronized (queue) {

queue.addLast(r);

queue.notify();

}

}

private class PoolWorker extends Thread {

public void run() {

Runnable r;

while (true) {

synchronized (queue) {

while (queue.isEmpty()) {

try {

queue.wait();

} catch (InterruptedException ignored) {

}

}

r = (Runnable) queue.removeFirst();

}

// If we don't catch RuntimeException,

// the pool could leak threads

try {

r.run();

} catch (RuntimeException e) {

// You might want to log something here

}

}

}

}

public static void main(String args[]) {

WorkQueue wq = new WorkQueue(3);

wq.execute(new Runnable() {

public void run() {

for (int i = 0; i < 10; i++) {

try {

Thread.sleep(1000);

} catch (Exception e) {}

System.out.println("this is a thread");

}

}

});

}

}

Java多线程和输入输出流

班级:13科技2班学号:201324131225 姓名:许耿宁 Java多线程和输入输出流 一、实验目的: 1.熟悉利用Thread类建立多线程方法。 2.熟悉利用Thread接口建立多线程方法。 3.熟悉Java的文件读写机制,练习输入输出流的使用。 二、实验内容: 1.阅读下列程序,分析并上机检验其功能。 public class DelayRunnable implements Runnable{ private static int count=0; private int no; private int delay; public DelayRunnable(){ count++; no=count; } public void run(){ try{ for (int i=0;i<10;i++){ delay=(int)(Math.random()*5000); Thread.sleep(delay); System.out.println("Thread "+no+" with a delay "+delay); } }catch(InterruptedException e){} } } class MyRunnable{ public static void main(String args[]){ DelayRunnable r1 = new DelayRunnable();

DelayRunnable r2 = new DelayRunnable(); Thread thread1=new Thread(r1); Thread thread2=new Thread(r2); thread1.start(); thread2.start(); try{ Thread.sleep(1000); }catch(InterruptedException e){ System.out.println("Thread wrong"); } } } 2.将上列程序利用Runnable接口改写,并上机检验。 3.创建简单的程序ThreeThread.java,该程序将创建三个线程,每个线程应当显示它所运行的时间(可以考虑使用Date类或Calendar类)。 4.键盘输入10个整数,从小到大进行排序。 5.接收键盘输入的字符串,用FileInputStream类将字符串写入文件,用 FileOutputStream类读出文件内容显示在屏幕上。 6.将一个文本文件的内容按行读出,每读出一行就顺序加上行号,并写入到另一个文件中。 三、实验要求: 1.通过实验掌握Thread 、Runnable使用方法; 2.程序必须能够实现多线程; 3.程序必须能够完成题目要求; 4.通过实验掌握文件输入输出流的使用方法; 5.程序必须能够从键盘接收字符串并保存在文件中; 6.程序必须能够读出文件内容显示在屏幕上; 7.写出实验报告。 四、实验代码及截图: 第一题: 在编译器上运行程序得到截图所示结果:

java多线程面试题

java多线程面试题 1.什么是多线程编程?什么时候使用? 多线程一般用于当一个程序需要同时做一个以上的任务。多线程通常用于GUI交互程序。一个新的线程被创建做一些耗时的工作,当主线程保持界面与用户的交互。 2.为什么wait(),notify()和notifyall()函数定义在Object类里面? 因为所有类都是继承于Object类,这样所有类就可以简单的进行多线程编程了。 3.wait()方法和sleep()方法有什么不同? sleep()方法执行后仍然拥有线程,只是延时。而wait方法放弃了线程控制,其它线程可以运行,想要再次运行是要重新开始。 4.Thread和Runnable有什么不同? JA V A线程控制着程序执行的主路径。当你用java命令调用JVM时,JVM创建了一个隐式线程来执行main方法。Thread类提供了主线程调用其它线程并行运行的机制。 Runnable接口定义了一个能被Thread运行的类。实现Runnable的类只需要实行run方法。可以很灵活的扩展现在的已经继承自其它父类的类。而thread则不可以,因为java 只允许继承一个父类。 Runnable可以共享数据,Thread是一个类,而Runnable是一个接口 5.我可以重载start()方法么? 可以重载,重载后还要重载run()方法, 9.编译运行下面的代码会发生什么? 1.public class Bground extends Thread{ 2.public static void main(String argv[]) 3.{ 4. Bground b = new Bground(); 5. b.run(); 6.} 7.public void start()

JAVA 面试题总览(书签完整版)

JAVA面试题总览 JAVA基础 1.JAVA中的几种基本数据类型是什么,各自占用多少字节。 2.String类能被继承吗,为什么。 3.String,Stringbuffer,StringBuilder的区别。 4.ArrayList和LinkedList有什么区别。 5.讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数, 字段,当new的时候,他们的执行顺序。 6.用过哪些Map类,都有什么区别,HashMap是线程安全的吗,并发下使用的Map是什么, 他们内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。 7.JAVA8的ConcurrentHashMap为什么放弃了分段锁,有什么问题吗,如果你来设计, 你如何设计。 8.有没有有顺序的Map实现类,如果有,他们是怎么保证有序的。 9.抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接 口么。 10.继承和聚合的区别在哪。 11.IO模型有哪些,讲讲你理解的nio,他和bio,aio的区别是啥,谈谈reactor模型。 12.反射的原理,反射创建类实例的三种方式是什么。 13.反射中,Class.forName和ClassLoader区别。 14.描述动态代理的几种实现方式,分别说出相应的优缺点。 15.动态代理与cglib实现的区别。 16.为什么CGlib方式可以对接口实现代理。 17.final的用途。 18.写出三种单例模式实现。 19.如何在父类中为子类自动完成所有的hashcode和equals实现?这么做有何优劣。 20.请结合OO设计理念,谈谈访问修饰符public、private、protected、default在应 用设计中的作用。 21.深拷贝和浅拷贝区别。 22.数组和链表数据结构描述,各自的时间复杂度。 23.error和exception的区别,CheckedException,RuntimeException的区别。 24.请列出5个运行时异常。 25.在自己的代码中,如果创建一个https://www.360docs.net/doc/e35588598.html,ng.String类,这个类是否可以被类加载器加 载?为什么。

JAVA线程程序设计(小时钟)实验报告(附完整代码)

线程程序设计 一、课题内容和要求 内容:设计和编写一个编写一个指针式时钟程序,应用线程实现时钟的走动。 要求:本实验旨在通过实验,培养学生将JAVA 线程的相关知识点(包括线程调度,线程同步等)有机结合并加以综合应用,在实验中设计多线程程序的能力。 二、设计思路分析 class Clock:一个指针式时钟的主类 class Layout: 添加窗口和时钟组件 class ClockPaint:定义时钟组件 三、概要设计 public class Clock extends JFrame { public static void main(String[] s) ; } class Layout extends JFrame { public Layout(); } class ClockPaint extends JPanel implements Runnable { int x, y, r; int h, m, s; double rad = Math.PI / 180; public ClockPaint(int x, int y, int r); public void paint(Graphics g); public void run(); } 时钟的绘制:

运行时钟: 四、详细设计 import java.awt.*; import javax.swing.*; import java.util.*; public class Clock extends JFrame { public static void main(String[] s) { new Layout(); } } class Layout extends JFrame {// 添加窗口和时钟组件public Layout() { ClockPaint cp = new ClockPaint(20, 20, 70); add(cp);

多线程常见面试题

1)现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完 后执行? T1.start(); T1.join(); T2.start(); T2.join(); T3.start() 2)11) 为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run() 方法? start()方法最本质的功能是从CPU中申请另一个线程空间来执行run()方法中的代码,它和当前的线程是两条线,在相对独立的线程空间运行 ,也就是说,如果你直接调用线程对象的run()方法,当然也会执行,但那是在当前线程中执行,run()方法执行完成后继续执行下面的代码.而调用start()方法后,run()方法的代码会和当前线程并发(单CPU)或并行(多CPU)执行。 调用线程对象的run方法不会产生一个新的线程 3)在java中wait和sleep方法的不同? sleep()睡眠时,保持对象锁,仍然占有该锁; 而wait()睡眠时,释放对象锁。 sleep()使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会; sleep()是Thread类的Static(静态)的方法;因此他不能改变对象的机锁,所以当在一个Synchronized块中调用Sleep()方法是,线程虽然休眠了,但是对象的机锁并木有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。 在sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。 wait()方法是Object类里的方法;当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超时时间到后还需要返还对象锁);其他线程可以访问; wait()使用notify或者notifyAlll或者指定睡眠时间来唤醒当前等待池中的线程。 wiat()必须放在synchronized block中,否则会在program runtime时扔出”https://www.360docs.net/doc/e35588598.html,ng.IllegalMonitorStateException“异常。 4)为什么wait, notify 和notifyAll这些方法不在thread类里面? 因为这些是关于锁的 而锁是针对对象的 锁用于线程的同步应用 决定当前对象的锁的方法就应该在对象中吧 我是这么理解的希望对你有帮助

java线程学习总结

java线程学习总结1(java thread培训总结1) 1.线程中一些基本术语和概念 (2) 1.1线程的几个状态 (2) 1.2 Daemon线程 (2) 1.3锁的定义 (2) 1.4死锁 (2) 1.5.Java对象关于锁的几个方法 (3) 1.6锁对象(实例方法的锁) (3) 1.7类锁 (4) 1.8.线程安全方法与线程不安全方法 (4) 1.9类锁和实例锁混合使用 (4) 1.10锁的粒度问题 (4) 1.11.读写锁 (5) 1.12 volatile (5) 2.线程之间的通讯 (5) 2.1屏障 (6) 2.2.锁工具类 (6) 2.3.条件变量 (6) 3. Java线程调度 (7) 3.1 Java优先级 (7) 3.2. 绿色线程 (7) 3.3 本地线程 (7) 3.4 Windows本地线程 (7) 3.5线程优先级倒置与继承 (8) 3.6循环调度 (8) 4.线程池 (8) 5工作队列 (9) 6.参考资料 (10)

1.线程中一些基本术语和概念 1.1线程的几个状态 初始化状态 就绪状态 运行状态 阻塞状态 终止状态 1.2 Daemon线程 Daemon线程区别一般线程之处是:主程序一旦结束,Daemon线程就会结束。 1.3锁的定义 为了协调多个并发运行的线程使用共享资源才引入了锁的概念。 1.4死锁 任何多线程应用程序都有死锁风险。当一组线程中的每一个都在等待一个只 有该组中另一个线程才能引起的事件时,我们就说这组线程死锁了。换一个说法就是一组线程中的每一个成员都在等待别的成员占有的资源时候,就可以说这组线程进入了死锁。死锁的最简单情形是:线程 A 持有对象X 的独占锁,并且在等待对象Y 的锁,而线程 B 持有对象Y 的独占锁,却在等待对象X 的锁。除非有某种方法来打破对锁的等待(Java 锁定不支持这种方法),否则死锁的线程将永远等下去。

精选大厂java多线程面试题50题

Java多线程50题 1)什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。 2)线程和进程有什么区别? 线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。更多详细信息请点击这里。 3)如何在Java中实现线程? https://www.360docs.net/doc/e35588598.html,ng.Thread类的实例就是一个线程但是它需要调用https://www.360docs.net/doc/e35588598.html,ng.Runnable接口来执行,由于线程类本身就是调用的 Runnable接口所以你可以继承https://www.360docs.net/doc/e35588598.html,ng.Thread类或者直接调用Runnable接口来重写run()方法实现线程。 4)Thread类中的start()和run()方法有什么区别? 这个问题经常被问到,但还是能从此区分出面试者对Java线程模型的理解程度。start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你

调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。 5)Java中Runnable和Callable有什么不同? Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。它们的主要区别是Callable的call()方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。 6)Java内存模型是什么? Java内存模型规定和指引Java程序在不同的内存架构、CPU 和操作系统间有确定性地行为。它在多线程的情况下尤其重要。 Java内存模型对一个线程所做的变动能被其它线程可见提供了保证,它们之间是先行发生关系。 ●线程内的代码能够按先后顺序执行,这被称为程序次序 规则。 ●对于同一个锁,一个解锁操作一定要发生在时间上后发 生的另一个锁定操作之前,也叫做管程锁定规则。 ●前一个对Volatile的写操作在后一个volatile的读操作之 前,也叫volatile变量规则。 ●一个线程内的任何操作必需在这个线程的start()调用之 后,也叫作线程启动规则。 ●一个线程的所有操作都会在线程终止之前,线程终止规

多线程总结

最近想将java基础的一些东西都整理整理,写下来,这是对知识的总结,也是一种乐趣。已经拟好了提纲,大概分为这几个主题: java线程安全,java垃圾收集,java并发包详细介绍,java profile和jvm性能调优。慢慢写吧。本人jameswxx原创文章,转载请注明出处,我费了很多心血,多谢了。关于java线程安全,网上有很多资料,我只想从自己的角度总结对这方面的考虑,有时候写东西是很痛苦的,知道一些东西,想用文字说清楚,却不是那么容易。我认为要认识 java线程安全,必须了解两个主要的点:java的内存模型,java的线程同步机制。特别是内存模型,java的线程同步机制很大程度上都是基于内存模型而设定的。从暂时写得比较仓促,后面会慢慢补充完善。 浅谈java内存模型 不同的平台,内存模型是不一样的,但是jvm的内存模型规范是统一的。java的多线程并发问题最终都会反映在java的内存模型上,所谓线程安全无非要控制多个线程对某个资源的有序访问或修改。java的内存模型,要解决两个主要的问题:可见性和有序性。我们都知道计算机有高速缓存的存在,处理器并不是每次处理数据都是取内存的。JVM定义了自己的内存模型,屏蔽了底层平台内存管理细节,对于java开发人员,要解决的是在jvm内存模型的基础上,如何解决多线程的可见性和有序性。 那么,何谓可见性?多个线程之间是不能互相传递数据通信的,它们之间的沟通只能通过共享变量来进行。Java内存模型(JMM)规定了jvm有主内存,主内存是多个线程共享的。当new一个对象的时候,也是被分配在主内存中,每个线程都有自己的工作内存,工作内存存储了主存的某些对象的副本,当然线程的工作内存大小是有限制的。当线程操作某个对象时,执行顺序如下: (1) 从主存复制变量到当前工作内存 (read and load) (2) 执行代码,改变共享变量值 (use and assign) (3) 用工作内存数据刷新主存相关内容 (store and write) JVM规范定义了线程对主存的操作指令:read,load,use,assign,store,write。当一个共享便变量在多个线程的工作内存中都有副本时,如果一个线程修改了这个共享变量,那么其他线程应该能够看到这个被修改后的值,这就是多线程的可见性问题。 那么,什么是有序性呢?线程在引用变量时不能直接从主内存中引用,如果线程工作内存中没有该变量,则会从主内存中拷贝一个副本到工作内存中,这个过程为read-load,完成后线程会引用该副本。当同一线程再度引用该字段时,有可能重新从主存中获取变量副本(read-load-use),也有可能直接引用原来的副本 (use),也就是说 read,load,use顺序可以由JVM实现系统决定。 线程不能直接为主存中中字段赋值,它会将值指定给工作内存中的变量副本(assign),完成后这个变量副本会同步到主存储区(store- write),至于何时同步过去,根据JVM实现系统决定.有该字段,则会从主内存中将该字段赋值到工作内存中,这个过程为read-load,完成后线程会引用该变量副本,当同一线程多次重复对字段赋值时,比如: for(int i=0;i<10;i++) a++; 线程有可能只对工作内存中的副本进行赋值,只到最后一次赋值后才同步到主存储区,所以assign,store,weite顺序可以由JVM实现系统决定。假设有一个共享变量x,线程a执行x=x+1。从上面的描述中可以知道x=x+1并不是一个原子操作,它的执行过程如下:

2019最新Java面试题,常见面试题及答案汇总

ava最新常见面试题+ 答案汇总 1、面试题模块汇总 面试题包括以下十九个模块:Java 基础、容器、多线程、反射、对象拷贝、Java Web 模块、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、Mybatis、RabbitMQ、Kafka、Zookeeper、MySql、Redis、JVM 。如下图所示: 可能对于初学者不需要后面的框架和JVM 模块的知识,读者朋友们可根据自己的情况,选择对应的模块进行阅读。 适宜阅读人群 需要面试的初/中/高级java 程序员 想要查漏补缺的人 想要不断完善和扩充自己java 技术栈的人 java 面试官 具体面试题 下面一起来看208 道面试题,具体的内容。 一、Java 基础 1.JDK 和JRE 有什么区别? 2.== 和equals 的区别是什么? 3.两个对象的hashCode()相同,则equals()也一定为true,对吗? 4.final 在java 中有什么作用? 5.java 中的Math.round(-1.5) 等于多少? 6.String 属于基础的数据类型吗? 7.java 中操作字符串都有哪些类?它们之间有什么区别? 8.String str="i"与String str=new String(“i”)一样吗? 9.如何将字符串反转? 10.String 类的常用方法都有那些? 11.抽象类必须要有抽象方法吗? 12.普通类和抽象类有哪些区别? 13.抽象类能使用final 修饰吗?

14.接口和抽象类有什么区别? 15.java 中IO 流分为几种? 16.BIO、NIO、AIO 有什么区别? 17.Files的常用方法都有哪些? 二、容器 18.java 容器都有哪些? 19.Collection 和Collections 有什么区别? 20.List、Set、Map 之间的区别是什么? 21.HashMap 和Hashtable 有什么区别? 22.如何决定使用HashMap 还是TreeMap? 23.说一下HashMap 的实现原理? 24.说一下HashSet 的实现原理? 25.ArrayList 和LinkedList 的区别是什么? 26.如何实现数组和List 之间的转换? 27.ArrayList 和Vector 的区别是什么? 28.Array 和ArrayList 有何区别? 29.在Queue 中poll()和remove()有什么区别? 30.哪些集合类是线程安全的? 31.迭代器Iterator 是什么? 32.Iterator 怎么使用?有什么特点? 33.Iterator 和ListIterator 有什么区别? 34.怎么确保一个集合不能被修改?

java学习整体总结

CoreJava部分 1 简述下java基本数据类型及所占位数,java基本数据类型:4类8种 整数类型:byte(1byte),short(2byte),int(4byte),long(8byte) 浮点类型:float(4byte),double(8byte) 字符类型:char(2byte) 逻辑类型:boolean(false/true 1byte) 2 说出5个启动时异常 RunTimeException ------NullPointerException ------ArrayIndexOutOfBoundsException ------ClassCastException ------NumberFormatException 3 HashMap 和HashTable的区别: 1HashMap 允许空键值对,HashTable不允许 2HashMap不是线程安全的,HashTable是 3HashMap直接实现Map接口,HashTable继承Dictionary类 4. ArrayList,Vector,LinkedList存储性能和区别 它们都实现了List接口 ArrayList和Vector都是基于数组实现的 LinkedList基于双向循环链表(查找效率低,添加删除容易) ArrayList不是线程安全的而Vector是线程安全的,所有速度上 ArrayList高于Vector 5. Collection和Collections的区别 Collection是集合类的上级接口,继承与他的接口主要有Set和List Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作。 6 List、Map、Set三个接口,存取元素时,各有什么特点? List以特定次序来持有元素,可有重复元素。 Set 无法持有重复元素,内部排序 Map保存key-value值,value可多值。 7 final,finally,finalize的区别 Final用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承 Finally 是异常处理语句结构的一部分,表示总是执行 Finalize 是Object类的一个方法,在垃圾收集时的其他资源回收,例如关闭文件等。 8 Overload和Override的区别。Overload的方法是否可以改变返回值的

15个Java多线程面试题及答案

15个Java多线程面试题及答案 1)现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行? 这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单,可以用join方法实现。 2)在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它? lock接口在多线程和并发编程中最大的优势是它们为读和写分别提 供了锁,它能满足你写像ConcurrentHashMap这样的高性能数据结构和有条件的阻塞。Java线程面试的问题越来越会根据面试者的回答来提问。芯学苑老师强烈建议在你在面试之前认真读一下Locks,因为当前其大量用于构建电子交易终统的客户端缓存和交易连接空间。 3)在java中wait和sleep方法的不同?

通常会在电话面试中经常被问到的Java线程面试问题。最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。 4)用Java实现阻塞队列。 这是一个相对艰难的多线程面试问题,它能达到很多的目的。第一,它可以检测侯选者是否能实际的用Java线程写程序;第二,可以检测侯选者对并发场景的理解,并且你可以根据这个问很多问题。如果他用wait()和notify()方法来实现阻塞队列,你可以要求他用最新的Java 5中的并发类来再写一次。 5)用Java写代码来解决生产者——消费者问题。 与上面的问题很类似,但这个问题更经典,有些时候面试都会问下面的问题。在Java中怎么解决生产者——消费者问题,当然有很多解决方法,我已经分享了一种用阻塞队列实现的方法。有些时候他们甚至会问怎么实现哲学家进餐问题。 6)用Java编程一个会导致死锁的程序,你将怎么解决?

java并发编程艺术总结.

1、并发编程的挑战 上下文切换:CPU通过时间片分配算法来循环执行任务,在切换任务的过程中,会保存上一个任务的状态,以便在下次切换回这个任务时,可以再加载这个任务的状态。 减少上下文切换的方法:无锁并发编程、CAS算法、使用最少线程和使用协程 2、Java并发机制的底层实现原理 Java代码编译后java字节码然后加载到JVM然后转化为CUP执行的汇编,java的并发依赖于JVM的实现与CPU的指令。 1. Volatile的应用 可见性:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。 后面还是详细介绍volatile关键字 2. synchronized的实现原理与应用 1) synchronized简介 synchronized在JVM中实现,JVM基于进入与退出Monitor对象来实现方法同步与代码块同步,在同步代码块前后分别形成monitorenter和monitorexit这两个字节码。synchronized的锁存放在java对象头里,在对象头里有关于锁的信息:轻量级锁,重量级锁,偏向锁。(对象头里还包括:GC标记、分代年龄、线程ID、HashCode等。) 2) 锁的介绍 级别从低到高:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,锁能升级不能降级,目的是提高获取锁和释放锁的效率。 偏向锁: 在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得。

为了让线程获得锁的代价更低而引入了偏向锁。 当一个线程访问同步块并获取锁(对象)时,会在对象头里记录偏向锁的线程ID。以后该线程进入与退出同步块时不需要进行CAS操作来加锁和解锁。如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会尝试消除它身上的偏向锁,将锁恢复到标准的轻量级锁。 轻量级锁: 线程通过CAS来获取锁(线程栈帧中有存储锁记录的空间,将Mask Word复制到锁记录中,然后尝试使用CAS将对象头中的Mask Word替换成指向锁记录的指针),如果成功,就获取锁,失败就尝试自旋来获取锁。 重量级锁: 为了避免在轻量级中无用的自旋(比如获取到锁的线程被阻塞住了),JVM可以将锁升级成重量级。当锁处于这个状态时,其他线程试图获取锁时,都会被阻塞住,当持有锁的线程释放锁之后会唤醒这些线程。 锁优点缺点使用场景 偏向锁加锁与解锁不需要 额外的消耗。线程存在竞争时, 会带来额外的锁撤 销的消耗 适用于只有一个 线程访问同步块 轻量级锁竞争的线程不会阻 塞,提高了程序的 响应速度始终得不到锁竞争 的线程,自旋消耗 CPU 追求响应时间, 同步块执行速度 非常快 重量级锁线程竞争不使用自 旋,不会消耗CPU 线程阻塞,响应时 间缓慢 追求吞吐量,同 步块执行时间较 长 3. 原子操作的实现原理 原子:不能被中断的一个或一系列操作。 在java中可以通过锁和循环CAS的方式来实现原子操作。 1) 使用循环CAS实现原子操作 利用处理器提供的CAS指令来实现,自旋CAS现在的基本思路就是循环进

多线程与并发面试题

多线程与并发面试题

JAVA多线程和并发基础面试问答 原文链接译文连接作者:Pankaj 译者:郑旭东校对:方腾飞 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,可是你依然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题。(校对注:非常赞同这个观点) Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它能够被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一个包含了不同的类和程序的单一进程。线程能够被称为轻量级进程。线程需要较少的资源来创立和驻留在进程中,而且能够共享进程中的资源。 2. 多线程编程的好处是什么?

在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。多个线程共享堆内存(heap memory),因此创立多个线程去执行一些任务会比创立多个进程更好。举个例子,Servlets比CGI更好,是因为Servlets支持多线程而CGI不支持。 3. 用户线程和守护线程有什么区别? 当我们在Java程序中创立一个线程,它就被称为用户线程。一个守护线程是在后台执行而且不会阻止JVM终止的线程。当没有用户线程在运行的时候,JVM关闭程序而且退出。一个守护线程创立的子线程依然是守护线程。 4. 我们如何创立一个线程? 有两种创立线程的方法:一是实现Runnable接口,然后将它传递给Thread的构造函数,创立一个Thread对象;二是直接继承Thread类。若想了解更多能够阅读这篇关于如何在Java中创立线程的文章。 5. 有哪些不同的线程生命周期?

线程编程方面笔试题

线程编程方面java笔试题 60、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用? 答:有两种实现方法,分别是继承Thread类与实现Runnable接口 用synchronized关键字修饰同步方法 反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。 61、sleep() 和 wait() 有什么区别? 答:sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。 62、同步和异步有何异同,在什么情况下分别使用他们?举例说明。 答:如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。 63、启动一个线程是用run()还是start()? 答:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。 64、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 答:不能,一个对象的一个synchronized方法只能由一个线程访问。 65、请说出你所知道的线程同步的方法。 答:wait():使一个线程处于等待状态,并且释放所持有的对象的lock。 sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉Interrupt edException异常。 notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。 Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。 66、多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么? 答:多线程有两种实现方法,分别是继承Thread类与实现Runnable接口 同步的实现方面有两种,分别是synchronized,wait与notify

Java线程总结

Java线程总结 在论坛上面常常看到初学者对线程的无可奈何,所以总结出了下面一篇文章,希望对一些正在学习使用java线程的初学者有所帮助。 首先要理解线程首先需要了解一些基本的东西,我们现在所使用的大多数操作系统都属于多任务,分时操作系统。正是由于这种操作系统的出现才有了多线程这个概念。我们使用的w indows,linux就属于此列。什么是分时操作系统呢,通俗一点与就是可以同一时间执行多个程序的操作系统,在自己的电脑上面,你是不是一边听歌,一边聊天还一边看网页呢?但实际上,并不上c pu在同时执行这些程序,c pu只是将时间切割为时间片,然后将时间片分配给这些程序,获得时间片的程序开始执行,不等执行完毕,下个程序又获得时间片开始执行,这样多个程序轮流执行一段时间,由于现在c pu 的高速计算能力,给人的感觉就像是多个程序在同时执行一样。 一般可以在同一时间内执行多个程序的操作系统都有进程的概念。一个进程就是一个执行中的程序,而每一个进程都有自己独立的一块内存空间,一组系统资源。在进程概念中,每一个进程的内部数据和状态都是完全独立的。因此可以想像创建并执行一个进程的系统开像是比较大的,所以线程出现了。在java中,程序通过流控制来执行程序流,程序中单个顺序的流控制称为线程,多线程则指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务。多线程意味着一个程序的多行语句可以看上去几乎在同一时间内同时运行。(你可以将前面一句话的程序换成进程,进程是程序的一次执行过程,是系统运行程序的基本单位) 线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制;但与进程不同的是,同类的多个线程是共享一块内存空间和一组系统资源,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈。所以系统在产生一个线程。或者在各个线程之间切换时,负担要比进程小的多,正因如此,线程也被称为轻负荷进程(light-w eight proc ess)。一个进程中可以包含多个线程。 多任务是指在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每个任务对应一个进程,同进程一样,一个线程也有从创建,运行到消亡的过程,称为线程的生命周期。用线程的状态(state)表明线程处在生命周期的哪个阶段。线程有创建,可运行,运行中,阻塞,死亡五中状态.通过线程的控制与调度可使线程在这几种状态间转化每个程序至少自动拥有一个线程,称为主线程。当程序加载到内存时,启动主线程。 [线程的运行机制以及调度模型] java中多线程就是一个类或一个程序执行或管理多个线程执行任务的能力,每个线程可以独立于其他线程而独立运行,当然也可以和其他线程协同运行,一个类控制着它的所有线程,可以决定哪个线程得到优先级,哪个线程可以访问其他类的资源,哪个线程开始执行,哪个保持休眠状态。 下面是线程的机制图: 线程的状态表示线程正在进行的活动以及在此时间段内所能完成的任务.线程有创建,可运行,运行中,阻塞,死亡五中状态。一个具有生命的线程,总是处于这五种状态之一: 1.创建状态 使用new运算符创建一个线程后,该线程仅仅是一个空对象,系统没有分配资源,称该线程处于创建状态(new thread) 2.可运行状态

多线程实验报告

宁波工程学院电信学院计算机教研室 实验报告 课程名称: Java 2 实验项目: 多线程实验 指导教师: **** 实验位置: 电信楼机房 姓 名: *** 学 号: **** 班 级: **** 日 期: 一、实验目的 1、掌握多线程编程的特点和工作原理; 2、掌握编写线程程序的方法 3、了解线程的调度和执行过程 4、掌握线程同步机理 二、实验环境 windows 记事本,java jdk 1.60版本,cmd 命令运行窗口 三、实验内容 实验一: 应用Java 中线程的概念写一个Java 程序(包括一个测试线程程序类TestThread ,一个Thread 类的子类PrintThread )。在测试程序中用子类PrintThread 创建2个线程,使得其中一个线程运行时打印10次“线程1正在运行”,另一个线程运行时打印5次“线程2正在运行 源程序: public class A { public static void main(String args[]) {

Test1 A1; Test2 A2; A1=new Test1(); A2=new Test2(); A1.start(); A2.start(); } } class PrintThread extends Thread { } class Test1 extends PrintThread { public void run() { for(int i=1;i<=10;i++) { System.out.println("线程1正在运行!"); } } } class Test2 extends PrintThread { public void run() { for(int i=1;i<=5;i++) { System.out.println("线程2正在运行!"); } } } 运行结果:

Java并发编程面试题整理与答案

2019年Java并发面试题整理(答案) 1、多线程的价值? (1)发挥多核CPU 的优势 多线程,可以真正发挥出多核CPU 的优势来,达到充分利用CPU 的目的,采用多线程的方式去同时完成几件事情而不互相干扰。 (2)防止阻塞 从程序运行效率的角度来看,单核CPU 不但不会发挥出多线程的优势,反而会因为在单核CPU 上运行多线程导致线程上下文的切换,而降低程序整体的效率。但是单核CPU 我们还是要应用多线程,就是为了防止阻塞。试想,如果单核CPU 使用单线程,那么只要这个线程阻塞了,比方说远程读取某个数据吧,对端迟迟未返回又没有设置超时时间,那么你的整个程序在数据返回回来之前就停止运行了。多线程可以防止这个问题,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。 (3)便于建模 这是另外一个没有这么明显的优点了。假设有一个大的任务A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务A 分解成几个小任务,任务B、任务C、任务D,分别建立程序模型,并通过多线程分别运行这几个任务,那就简单很多了。 2、实现可见性的方法有哪些? synchronized 或者Lock:保证同一个时刻只有一个线程获取锁执行代码,锁释放之前把最新的值刷新到主内存,实现可见性。 3、并发编程三要素? (1)原子性 原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行。 (2)可见性 可见性指多个线程操作一个共享变量时,其中一个线程对变量进行修改后,其他线程可以立即看到修改的结果。 (3)有序性 有序性,即程序的执行顺序按照代码的先后顺序来执行。

多线程面试题

线程或者说多线程,是我们处理多任务的强大工具。线程和进程是不同的,每个进程都是一个独立运行 的程序,拥有自己的变量,且不同进程间的变量不能共享;而线程是运行在进程内部的,每个正在运行的进程至少有一个线程,而且不同的线程之间可以在进程范围内共享数据。也就是说进程有自己独立的存储 空间,而线程是和它所属的进程内的其他线程共享一个存储空间。线程的使用可以使我们能够并行地处理 一些事情。线程通过并行的处理给用户带来更好的使用体验,比如你使用的邮件系统(outlook、Thunderbird、foxmail等),你当然不希望它们在收取新邮件的时候,导致你连已经收下来的邮件都无法阅读,而只能等待收取邮件操作执行完毕。这正是线程的意义所在。 实现线程的方式 实现线程的方式有两种: 1.继承https://www.360docs.net/doc/e35588598.html,ng.Thread,并重写它的run()方法,将线程的执行主体放入其中。 2. 实现https://www.360docs.net/doc/e35588598.html,ng.Runnable接口,实现它的run()方法,并将线程的执行主体放入其中。 这是继承Thread类实现线程的示例: [java]view plaincopyprint? 1.public class ThreadTest extends Thread {
public void run() {
// 在 这里编写线程执行的主体
// do something
}
} 这是实现Runnable接口实现多线程的示例: [java]view plaincopyprint? 1.public class RunnableTest implements Runnable {
public void run() {
// 在这里编写线程执行的主体
// do something
}
} 这两种实现方式的区别并不大。继承Thread类的方式实现起来较为简单,但是继承它的类就不能再继承 别的类了,因此也就不能继承别的类的有用的方法了。而使用是想Runnable接口的方式就不存在这个问 题了,而且这种实现方式将线程主体和线程对象本身分离开来,逻辑上也较为清晰,所以推荐大家更多地 采用这种方式。 如何启动线程

相关文档
最新文档