线程(Thread)是一个程序内部的一条执行流程。
程序中如果只有一条执行流程,那这个程序就是单线程的程序。
多线程是指从软硬件上实现的多条执行流程的技术(多条线程由cpu负责调度执行)。
方式一:继承Thread
①定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
②创建MyThread类的对象
③调用线程对象的start()方法启动线程(启动后还是执行run方法的)
//(1)让自定义的MyThread继承Thread线程类【自定义的类也就具备线程的特性】
public class MyThread extends Thread {
//(2)想要声明自定义的线程执行的时候到底执行什么代码,主动重写父类的run方法
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
System.out.println("【自定义线程】的run方法执行了第" + i + "次!");
}
}
}
public class ThreadTest1 {
public static void main(String[] args) {
//(3)创建自定义线程类对象并调用start方法启动线程
MyThread myThread = new MyThread();
myThread.start();
//补:在自定义线程启动之后,继续编写代码让主线程执行
for (int i = 1; i <= 20; i++) {
System.out.println("【主线程】的run方法执行了第" + i + "次!");
}
}
}
方式一优缺点
优点:编码简单
缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展。
方式二:实现Runnable接口
①定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
②创建MyRunnable任务对象
③把MyRunnable任务对象交给Thread处理。
public class ThreadTest2 {
public static void main(String[] args) {
//(3)创建MyRunnable线程任务对象 【和线程还没有关系】
MyRunnable myRunnable = new MyRunnable();
//(4)创建Thread线程对象,并且线程任务作为构造方法的参数传递【枪:√ 弹夹:√】
Thread t = new Thread(myRunnable);
t.start();
//补:在自定义线程启动之后,继续编写代码让主线程执行
for (int i = 1; i <= 20; i++) {
System.out.println("【主线程】的run方法执行了第" + i + "次!");
}
}
}
方式二优缺点:
优点:任务类只是实现接口,可以继续继承其他类,实现其他接口,扩展性强。
缺点:需要多一个Runnable对象。
前两种线程创建文件都存在的一个问题
假如线程执行完毕后有一些数据需要返回,他们重写的run方法均不能直接返回结果。
解决(多线程的第三种创建方式)
利用Callable,FutureTask类型实现。
①创建任务对象
定义一个类实现Callable接口,重写call方法,封装要做的事情和要返回的数据。
把Callable类型的对象封装成FutureTask对象(线程任务对象)。
②把线程任务对象封装成Thread对象。
③调用Thread对象的start方法启动线程。
④线程执行完毕后,通过FutureTask对象的的get方法去获取线程任务执行的结果。
public class ThreadTest4 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//(3)Thread类不支持直接传递一个Callable线程任务对象【封装FutureTask对象并且将Callable线程任务作为构造参数传递】
MyCallable myCallable = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
//(4)创建Thread类对象并且将FutureTask作为参数传递
Thread t = new Thread(futureTask);
t.start();
//★(5)通过futureTask对象获取结果
Integer result = futureTask.get();
System.out.println("带有返回值的线程任务执行完成后返回的结果是:" + result);
//补:在自定义线程启动之后,继续编写代码让主线程执行
for (int i = 1; i <= 20; i++) {
System.out.println("【主线程】的run方法执行了第" + i + "次!");
}
}
}
方式三优缺点:
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果。
缺点:编码复杂一点。

public void run() 线程的任务方法
public void start() 启动线程
public Strign getName() 获取1当前线程名称,线程名称默认是Thread-索引
public void setName(String name) 为线程设置名称
public static Thread currentThread() 获取当前执行的线程对象
public static void sleep(long time) 让当前执行的线程休眠多少毫秒后,再继续执行
public void join() 让调用这个方法的线程先执行完
public class MyRunRunable implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 200; i++) {
//在打印的时候,想要【获取到执行当前这行代码的线程】的线程名称
//通过★Thread.currentThread():获取当前执行此方法的线程对象
System.out.println(Thread.currentThread().getName() + "已经跑了" + i + "米!");
}
}
}
public class ThreadTest5 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
String threadName = Thread.currentThread().getName();
System.out.println("【主线程名称】:" + threadName);
MyRunRunable myRunRunable = new MyRunRunable();
//线程起名方式(1):通过线程对象调用setName方法传递名称
Thread t1 = new Thread(myRunRunable);
t1.setName("张二狗");
//思考:模拟两个人跑 => 两个线程跑【跑的逻辑一样 所以使用同一个线程任务】不会干扰【底层:线程栈 线程执行过程中产生的变量数据都在线程栈中保存】
//线程起名方式(2):通过new Thread构造方法的时候,将参数一作为线程任务,参数二作为线程名称
Thread t2 = new Thread(myRunRunable, "刘铁柱");
t1.start();
//t1.join(); 【让调用此方法的线程先执行完:插队】
t2.start();
}
}
什么是线程安全问题?
多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题。
取钱的线程安全问题
场景:小明和小红是一对夫妻,他们有一个共同的账户,余额是10万元,如果小明和小红同时来取钱,并且2人各自都在取钱10万元,可能会出现什么问题呢?

线程安全问题出现的原因?
存在多个线程在同时执行
多个线程同时访问一个共享资源
存在修改共享资源的情况
定义一个账号类
package com.itheima.safe;
import java.time.LocalTime;
public class Account {
private String accoundId;
private Integer money;
//取钱:takeMoney
public void takeMoney(Integer money) {
//获取当前取钱的线程名称
String name = Thread.currentThread().getName();
System.out.println(LocalTime.now() + " " + name + "准备开始取钱!");
if (this.money >= money) {
System.out.println(LocalTime.now() + " " + name + "取出了" + money + "元!");
this.money -= money;
} else {
System.out.println(LocalTime.now() + " " + name + "余额不足!");
}
System.out.println(LocalTime.now() + " 账户的余额是:" + this.money + "元!");
}
public String getAccoundId() {
return accoundId;
}
public void setAccoundId(String accoundId) {
this.accoundId = accoundId;
}
public Integer getMoney() {
return money;
}
public void setMoney(Integer money) {
this.money = money;
}
public Account() {
}
public Account(String accoundId, Integer money) {
this.accoundId = accoundId;
this.money = money;
}
}
定义线程类
public class TakeMoneyRunnable implements Runnable {
//线程任务需要访问到Account账户对象【将账户对象作为线程任务的构造方法 并且只给出一个有参构造】
private Account account;
public TakeMoneyRunnable(Account account) {
this.account = account;
}
@Override
public void run() {
account.takeMoney(100000);
}
}
测试类
package com.itheima.safe;
public class TakeMoneyThreadTest {
public static void main(String[] args) {
Account account = new Account("CHINA-BANK-62261728738", 100000);
//创建线程任务【由于两个线程的逻辑一样 只需要一个线程任务】
TakeMoneyRunnable takeMoneyRunnable = new TakeMoneyRunnable(account);
//创建线程对象并且传递线程任务和线程名称
Thread t1 = new Thread(takeMoneyRunnable, "张二狗");
Thread t2 = new Thread(takeMoneyRunnable, "王美丽");
t1.start();
t2.start();
}
}
线程同步的思想
让多个线程实现先后依次访问共享资源,这样就解决了安全问题。
作用:把访问共享资源的核心代码给上锁,以此保证线程安全。

原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程次才可以来执行。
对线程安全改造
public void takeMoney(Integer money) {
String name = Thread.currentThread().getName();
System.out.println(LocalDateTime.now() + "" + name + "准备开始取钱");
//(同步代码块)加锁
synchronized (this) {
if (this.money >= money) {
System.out.println(LocalDateTime.now() + "" + name + "取出了" + money + "元");
this.money -= money;
} else {
System.out.println(LocalDateTime.now() + "" + name + "余额不足");
}
System.out.println(LocalDateTime.now() + "账户的余额是" + this.money + "元");
}
}
同步锁的注意事项
对于当前同时执行的线程来说,同步锁必须是同一把锁(同一个对象),否则会出bug。
锁对象的使用规范
建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象。
对于静态方法建议使用字节码(类名.class)对象作为锁对象。
作用:把访问共享资源的核心代码给上锁,以此保证线程安全。

原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程次才可以来执行。
对线程安全改造
//同步方法加锁(修饰符后面,放回值类型前面)
public synchronized void takeMoney(Integer money) {
String name = Thread.currentThread().getName();
System.out.println(LocalDateTime.now() + "" + name + "准备开始取钱");
if (this.money >= money) {
System.out.println(LocalDateTime.now() + "" + name + "取出了" + money + "元");
this.money -= money;
} else {
System.out.println(LocalDateTime.now() + "" + name + "余额不足");
}
System.out.println(LocalDateTime.now() + "账户的余额是" + this.money + "元");
}
}
同步方法底层原理
同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。
如果方法是实例方法:同步方法默认用this作为的锁对象。
如果方法是静态方法:同步方法默认用类名.class作为的锁对象。
1.同步方法是如何保证线程安全的?
对出现问题的核心方法使用**synchronized修饰**
每次只能一个线程占锁进入访问
2.同步方法的同步锁对象的原理?
对于实例方法默认使用**this作为锁对象。**
对于静态方法默认使用**类名.class对象作为锁对象。**
Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。
Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。
对线程安全改造
private static final Lock LOCK = new ReentrantLock();
public void takeMoney(Integer money) {
LOCK.lock();
try {
String name = Thread.currentThread().getName();
System.out.println(LocalDateTime.now() + "" + name + "准备开始取钱");
if (this.money >= money) {
System.out.println(LocalDateTime.now() + "" + name + "取出了" + money + "元");
this.money -= money;
} else {
System.out.println(LocalDateTime.now() + "" + name + "余额不足");
}
System.out.println(LocalDateTime.now() + "账户的余额是" + this.money + "元");
}finally {
LOCK.unlock();
}
}
什么是线程池?
线程池就是一个可以复用线程的技术。
不使用线程池的问题
用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理,而创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程,这样会严重影响系统的性能。
线程池的工作原理

如何得到线程池对象?
方式一:使用ExecutorService的实现类ThreadPoolExecutor创建一个线程池对象。

方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。
ThreadPoolExecutor**构造器**

参数一:corePoolSize : 指定线程池的核心线程数量。
参数二:maximumPoolSize:指定线程池的最大线程数量。
参数三:keepAliveTime :指定临时线程的存活时间。
参数四:unit:指定临时线程存活的时间单位(秒、分、时、天)
参数五:workQueue:指定线程池的任务队列。
参数六:threadFactory:指定线程池的线程工厂。
参数七:handler:指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了该怎么处理)
public class PoolDemo1 {
public static void main(String[] args) {
//基于ThreadPoolExecutor的构造方法创建线程池对象
//核心线程:【线程任务:Cpu密集型(运算):当前机器Cpu的核心数+1 Runtime.getRuntime().availableProcessors()+1】
//核心线程:【线程任务:IO密集型(读写):当前机器Cpu的核心数*2 Runtime.getRuntime().availableProcessors()*2】
//ThreadFactory:线程工厂 【Exectors.defaultThreadFactory】 获取默认的线程工厂
ThreadPoolExecutor pool = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors() + 1, 15, 40L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
//可以基于线程池规范接口的execute方法提交线程任务交给线程池执行
for (int i = 1; i <= 25; i++) {
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行了线程任务!");
}
});
}
//AbortPolicy:默认丢弃新任务并且抛出异常
//DiscardPolicy:默认丢弃新任务并且不抛出异常
//DiscardOldestPolicy:默认将等待时间最长的任务丢弃,并且让新任务添加到队列中
//CallerRunsPolicy:使用主线程执行新任务绕过当前线程池
//线程池一旦提交任务就持久运行【想要关闭调用shutdown/shutdownNow】
pool.shutdown();
}
}
线程池的注意事项
1、临时线程什么时候创建?
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
2、什么时候会开始拒绝新任务?
核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。
线程池如何处理Runnable任务?
使用ExecutorService的方法:
void execute(Runnable target)
线程池如何处理Callable任务,并得到任务执行完后返回的结果?
使用ExecutorService的方法:
Future<T> submit(Callable<T> command)
并发的含义
进程中的线程是由CPU负责调度执行的,但CPU能同时处理线程的数量有限,为了保证全部线程都能往前执行,CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发。
并行的理解
在同一个时刻上,同时有多个线程在被CPU调度执行。
简单说说多线程是怎么执行的?
并发:CPU分时轮询的执行线程。
并行:同一个时刻多个线程同时在执行。
Java总共定义了6种状态
6种状态都定义在Thread类的内部枚举类中。



我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我正在尝试使用ruby编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?
我是ruby的新手,我认为重新构建一个我用C#编写的简单聊天程序是个好主意。我正在使用Ruby2.0.0MRI(Matz的Ruby实现)。问题是我想在服务器运行时为简单的服务器命令提供I/O。这是从示例中获取的服务器。我添加了使用gets()获取输入的命令方法。我希望此方法在后台作为线程运行,但该线程正在阻塞另一个线程。require'socket'#Getsocketsfromstdlibserver=TCPServer.open(2000)#Sockettolistenonport2000defcommandsx=1whilex==1exitProgram=gets.chomp
我有一个使用PDFKit呈现网页的pdf版本的Rails应用程序。我使用Thin作为开发服务器。问题是当我处于开发模式时。当我使用“bundleexecrailss”启动我的服务器并尝试呈现任何PDF时,整个过程会陷入僵局,因为当您呈现PDF时,会向服务器请求一些额外的资源,如图像和css,看起来只有一个线程.如何配置Rails开发服务器以运行多个工作线程?非常感谢。 最佳答案 我找到的最简单的解决方案是unicorn.geminstallunicorn创建一个unicorn.conf:worker_processes3然后使用它:
所以,Ruby1.9.1现在是declaredstable.Rails应该与它一起工作,并且正在慢慢地将gem移植到它。它具有native线程和全局解释器锁(GIL)。自从GIL到位后,原生线程是否比1.9.1中的绿色线程有任何优势? 最佳答案 1.9中的线程是原生的,但它们被“放慢了速度”,一次只允许一个线程运行。这是因为如果线程真的并行运行,它会混淆现有代码。优点:IO现在在线程中是异步的。如果一个线程阻塞在IO上,那么另一个线程将继续执行直到IO完成。C扩展可以使用真正的线程。缺点:任何非线程安全的C扩展都可能存在使用Thre
我在一个ruby文件中有一个函数可以像这样写入一个文件File.open("myfile",'a'){|f|f.puts("#{sometext}")}这个函数在不同的线程中被调用,使得像上面这样的文件写入不是线程安全的。有谁知道如何以最简单的方式使这个文件写入线程安全?更多信息:如果重要的话,我正在使用rspec框架。 最佳答案 您可以通过File#flock给锁File.open("myfile",'a'){|f|f.flock(File::LOCK_EX)f.puts("#{sometext}")}
我编写了几个类来控制我想如何处理多个网站,两者都使用类似的方法(即登录、刷新)。每个类都打开自己的WATIR浏览器实例。classSite1definitialize@ie=Watir::Browser.newenddeflogin@ie.goto"www.blah.com"endend无线程的main中的代码示例如下require'watir'require_relative'site1'agents=[]agents这工作正常,但在当前代理完成登录之前不会移动到下一个代理。我想合并多线程来处理这个问题,但似乎无法让它工作。require'watir'require_relative
代码:threads=[]Thread.abort_on_exception=truebegin#throwexceptionsinthreadssowecanseethemthreadseputs"EXCEPTION:#{e.inspect}"puts"MESSAGE:#{e.message}"end崩溃:.rvm/gems/ruby-2.1.3@req/gems/activesupport-4.1.5/lib/active_support/dependencies.rb:478:inload_missing_constant':自动加载常量MyClass时检测到循环依赖稍加研究后,
任何人都可以推荐任何详细介绍Ruby多线程/多处理的复杂性的好的多线程/处理书籍/网站吗?我尝试使用ruby线程,基本上在1.9vm上的无死锁代码中它在jruby中遇到了死锁。是的,我意识到差异很大(jruby没有GIL),但我想知道是否有用于ruby中多线程编程的策略或类集,我只需要继续阅读。旁注:从java到ruby必须定义是否需要重新输入锁,这有点奇怪。 最佳答案 如果你使用Ruby1.9,你可以试试Fiber,它是Ruby中线程的一大改进http://ruby-doc.org/core-1.9/classes/F
我想从不同线程调用一个公共(public)枚举器。当我执行以下操作时,enum=(0..1000).to_enumt1=Thread.newdopenum.nextsleep(1)endt2=Thread.newdopenum.nextsleep(1)endt1.joint2.join它引发了一个错误:Fibercalledacrossthreads.当enum在从t1调用一次后从t2调用时。为什么Ruby设计为不允许跨线程调用枚举器(或纤程),以及是否有其他方法可以提供类似的功能?我猜测枚举器/纤程上的操作的原子性在这里是相关的,但我不完全确定。如果这是问题所在,那么在使用时独占锁定