多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。
就是在单个程序中同时运行多个线程来完成不同的工作。
Java源程序和字节码文件被称为“程序” (Program),是一个静态的概念。
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
Java使用java.lang.Thread
类代表线程,所有的线程对象都必须是Thread类或其子类的实例。
Java中通过继承Thread类来创建并启动多线程的步骤如下:
自定义线程类:
public class MyThread extends Thread {
//定义指定线程名称的构造方法
public MyThread(String name) {
//调用父类的String参数的构造方法,指定线程的名称
super(name);
}
/**
* 重写run方法,完成该线程执行的逻辑
*/
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName()+":正在执行!"+i);
}
}
}
测试类:
public class Demo01 {
public static void main(String[] args) {
//创建自定义线程对象
MyThread mt = new MyThread("新的线程!");
//开启新线程
mt.start();
//在主方法中执行for循环
for (int i = 0; i < 10; i++) {
System.out.println("main线程!"+i);
}
}
}
构造方法:
public Thread()
:分配一个新的线程对象。
public Thread(String name)
:分配一个指定名字的新的线程对象。
public Thread(Runnable target)
:分配一个带有指定目标新的线程对象。
public Thread(Runnable target,String name)
:分配一个带有指定目标新的线程对象并指定名字。
常用方法:
public String getName()
:获取当前线程名称。
public void start()
:导致此线程开始执行;Java虚拟机调用此线程的run方法。
public void run()
:此线程要执行的任务在此处定义代码。
public static void sleep(long millis)
:使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static Thread currentThread()
:返回对当前正在执行的线程对象的引用
采用java.lang.Runnable也是非常常见的一种,我们只需要重写run方法即可。
步骤如下:
自定义线程类:
public class MyRunnable implements Runnable {
@Override
public void run(){
for(inti=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+""+i);
}
}
}
测试类:
public class Demo{
public static void main(String[]args){
//创建自定义类对象线程任务对象
MyRunnable mr = new MyRunnable();
//创建线程对象
Thread t = new Thread(mr,"小强");
t.start();
for(inti=0;i<20;i++){
System.out.println("旺财"+i);
}
}
}
实现Runnable接口比继承Thread类所具有的优势:
适合多个相同的程序代码的线程去共享同一个资源。
可以避免java中的单继承的局限性。
增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
/**
* 用Lambda表达式来创建多线程
*/
public class Thread004Lambda {
public static void main(String[] args) {
Thread t = new Thread(()->{
System.out.println("我是一个线程对象");
});
t.start();//启动线程
System.out.println("我是主线程");
}
}
死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个。一个是正常运行的线程完成了它的全部工作;另一个是线程被强制性地终止,如通过执行stop或destroy方法来终止一个线程。
不要调用 stop destory 方法 ,太暴力,一盆冷水让其停止。
自定义类:
/**
*
* 终止线程的套路
* 1、加入标识
* 2、线程体的执行需要依赖标识
* 3、对外提供一个可以修改标识的方法
* 4、外部适当的时机调用方法修改标识
*/
class MyThread03 implements Runnable{
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("在和小美聊天,在说第"+(i++)+"句话");
if(!flag){
break;
}
}
}
public void stopThread(){
flag = false;
}
}
测试类:
public class Thread005 {
public static void main(String[] args) throws Exception{
MyThread03 mt = new MyThread03();
Thread t = new Thread(mt);
t.start();//开启线程
Thread.sleep(2000);
mt.stopThread();
}
}
注意:优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高后调用优先级低的线程。
自定义类:
class FatherThread implements Runnable {
@Override
public void run() {
System.out.println("爸爸想抽烟,发现烟抽完了");
System.out.println("爸爸让儿子去买包红塔山");
Thread son = new Thread(new SonThread());
son.start(); // 启动新的线程
System.out.println("爸爸等儿子买烟回来");
try {
son.join(); // 一直傻傻地等
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("可以抽烟了");
}
}
class SonThread implements Runnable {
public void run() {
System.out.println("儿子出门去买烟");
System.out.println("儿子买烟需要10分钟");
try {
for (int i = 1; i <=10;i++) {
System.out.println(Thread.currentThread().getName() + " 第" + i + "分钟");
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 儿子买烟回来了");
}
}
测试类:
public class Demo008Thread {
public static void main(String[] args)throws Exception {
Thread t1 = new Thread(new SonThread(),"t1");
Thread t2 = new Thread(new SonThread(),"t2");
// int p = t1.getPriority();获取优先级
t1.setPriority(10);//设置线程优先级
t2.setPriority(1);
t1.start();
t2.start();
Thread.sleep(1000);
//System.out.println(t.isAlive()); // 查看线程是否还活着
}
}
当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。
要解决上述多线程并发访问一个资源的安全性问题:也就是解决重复票与不存在票问题,Java中提供了同步机制(synchronized)来解决。
格式:
synchronized(同步锁){
需要同步操作的代码
}
同步锁:
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.
格式:
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步锁是谁?
综合案例:
/**
* 账户类
*/
class Account {
String aname; // 账户名
int money; // 账户余额
public Account(String aname, int money) {
this.aname = aname;
this.money = money;
}
}
class Drawing implements Runnable{
Account account = null;
int money = 0; // 准备取钱的金额
int acturalMoney = 0; // 实际取走的钱
public Drawing(Account account, int money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
synchronized (account){//同步代码块
// 如果余额不够, 取不了钱
if (account.money < money) {
System.out.println(" 余额不足, 没去走钱");
return;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money -= money; // 取了钱,余额变少
acturalMoney = money; // 取出来的钱
System.out.println(Thread.currentThread().getName() + "取了 " + acturalMoney + " 还剩 " + account.money);
}
}
}
测试类:
/**
* 两个人操作同一个账户
*/
public class Demo010Thread {
public static void main(String[] args) throws Exception{
// 开户
Account account = new Account("高富帅",100);
System.out.println(account.money);
Drawing d = new Drawing(account,90);
// 你去取钱
Thread t = new Thread(d,"你" );
Thread t1 = new Thread(d,"女朋友" );
t.start();
t1.start();
Thread.sleep(1500);
System.out.println(account.money);
}
}
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
如何解决死锁问题: