Java线程
Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。
这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。
线程生命周期
创建线程的方法:
- 继承Thread类
- 实现Runnable 接口
方法名 | 方法描述 |
---|---|
public void start() | 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 |
public void run() | 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 |
public final void setName(String name) | 改变线程名称,使之与参数 name 相同。 |
public final void setPriority(int priority) | 更改线程的优先级。注意:先设置优先级,在启动start()。 |
public final void setDaemon(boolean on) | 将该线程标记为守护线程或用户线程。 |
public final void join(long millisec) | 等待该线程终止的时间最长为 millis 毫秒。 |
public final void join() | 插队,等待插队的线程执行完毕才能执行其他线程。 |
public void interrupt() | 中断线程。 |
public final boolean isAlive() | 测试线程是否处于活动状态。 |
public State getState() | 查看线程状态, |
方法名 | 方法描述 |
---|---|
public static void yield() | 暂停当前正在执行的线程对象,并执行其他线程。 |
public static void sleep(long millisec) | 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 |
public static boolean holdsLock(Object x) | 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 |
public static Thread currentThread() | 返回对当前正在执行的线程对象的引用。 |
public static void dumpStack() | 将当前线程的堆栈跟踪打印至标准错误流。 |
1.继承Thread的例子
public class Thread01 extends Thread{
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println("我是线程------"+i);
}
}
public static void main(String[] args) {
Thread01 thread01 = new Thread01();
//调起线程进入就绪状态,线程之间交互执行,由cpu决定(主线程会优先获取资源执行(1次或多次),之后还是看cpu调度资源进行交互执行)
//不要调用run方法,不然就无法体现多线程了。
thread01.start();
for (int i = 0; i < 200; i++) {
System.out.println("我是主线程"+i);
}
}
}
2. 实现Runnable接口的例子
public class Runnable01 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我是线程-------"+i);
}
}
public static void main(String[] args) {
Runnable01 runnable01 = new Runnable01();
//需要new一个Thread,由于start()方法是Thread的方法,runnable并没有
Thread thread = new Thread(runnable01);
//开启线程
thread.start();
for (int i = 0; i < 200; i++) {
System.out.println("我是主线程"+i);
}
}
}
- 采用实现 Runnable接口的方式创建多线程时,线程类只是实现了 Runnable 接口,还可以继承其他类。
- 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
3. sleep方法,线程休眠
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
- 一般用来模拟网络延迟请求,倒计时等
例子:
import javax.xml.crypto.Data;
import java.text.SimpleDateFormat;
import java.util.Date;
//通过sleep()来模拟倒计时和当前时间
public class TenDoown {
public static void main(String[] args) throws InterruptedException {
TenDoown tenDoown = new TenDoown();
// tenDoown.tenDown();
tenDoown.currentTime();
}
//倒计时十秒
public void tenDown() throws InterruptedException {
for (int i = 10; i >=0; i--) {
Thread.sleep(1000);
System.out.println(i);
if(i==0){
System.out.println("时间到!!!");
break;
}
}
}
//获取当前时间,每秒更新
public void currentTime() throws InterruptedException {
Date date = new Date(System.currentTimeMillis());//当前系统时间
System.out.println(date);
while (true){
Thread.sleep(1000);//线程每一秒执行
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
date=new Date(System.currentTimeMillis());//更新当前时间
}
}
}
4.线程礼让yield
暂停当前正在执行的线程对象,使其从运行状态->就绪状态,然后与就绪状态下的其他线程重新竞争资源(所以线程礼让不一定成功,可能下一次还是礼让线程被调用)
/*
* 线程礼让yield,不一定成功。
* 礼让只是当前线程从运行状态->就绪状态,然后与其他线程再重新竞争资源
* */
public class ThreadYield {
public static void main(String[] args) {
MyThread myThread = new MyThread();
new Thread(myThread,"线程a").start();
new Thread(myThread,"线程b").start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始");
//线程礼让
Thread.yield();
System.out.println(Thread.currentThread().getName()+"结束");
}
}
5. 线程插队 join
线程插队,会让当前插队的线程还没执行完的线程,执行完毕后,才给其他 线程资源
public class ThreadJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("我插一下队"+i);
}
}
public static void main(String[] args) throws InterruptedException {
ThreadJoin threadJoin = new ThreadJoin();
Thread thread = new Thread(threadJoin);
thread.start();
for (int i = 0; i < 200; i++) {
System.out.println("排排队"+i);
if(i==50){
thread.join();//当i=12时刻,线程插队执行,将剩下的全部在这里执行完毕
}
}
}
}
6. 线程种类
- java中线程有用户线程(一般我们写的默认都是,main就是一个用户线程)和守护线程(后台记录操作日志,垃圾回收等)
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
package com.suzezhi.thread;
public class Thread01 {
public static void main(String[] args) {
ThreadUser user = new ThreadUser();
new Thread(user).start();
//守护线程
ThreadDemon threadDemon = new ThreadDemon();
Thread thread = new Thread(threadDemon);
thread.setDaemon(true);//设置为守护线程
thread.start();
}
}
//用户线程
class ThreadUser implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("我是用户线程");
}
}
}
//守护线程
class ThreadDemon implements Runnable{
@Override
public void run() {
while (true){
System.out.println("我是守护线程");
}
}
}
7. 线程同步synchronized
- 同步进行增删改的对象
- 同步方法,执行在方法返回值类型前加上synchronized
- 同步块:synchronized(obj){},obj是监听的对象,把要监听的代码放到{}里面
//同步方法
package com.suzezhi.syn;
//模拟买票
public class BuyTricket implements Runnable {
private int nums=50;
boolean flag=true;//用来中断线程的状态
@Override
public void run() {
while (flag){
buy();
--nums;
}
}
//买票的方法,为了防止出现插队的情况(就是保证线程安全),对方法进行同步
public synchronized void buy(){
if(nums<=0){
flag=false;
return;
}
try {
Thread.sleep(100);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"买到票号:"+nums);
}
public static void main(String[] args) {
BuyTricket buyTrick = new BuyTricket();
new Thread(buyTrick,"学生").start();
new Thread(buyTrick,"老师").start();
new Thread(buyTrick,"老六").start();
}
}
//同步块
package com.suzezhi.syn;
public class Bank {
public static void main(String[] args) {
Account account = new Account("房钱", 500);
Drawing self = new Drawing(account, 300, "自己");
Drawing wife = new Drawing(account, 400, "妻子");
self.start();
wife.start();
}
}
//账户
class Account{
String name;
int money;
public Account(String name, int money) {
this.name = name;
this.money = money;
}
}
// 模拟银行取钱
class Drawing extends Thread{
private Account account;
//取钱
private int drawingMoney;
//手上的钱
private int nowMoney;
public Drawing(Account account,int drawingMoney,String name){
super(name);
this.account=account;
this.drawingMoney=drawingMoney;
}
@Override
public void run() {
synchronized (account){
if(account.money-drawingMoney<0){
System.out.println(this.getName()+":账户余额不足!!");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//账户额度
account.money=account.money-drawingMoney;
//手上的钱
nowMoney=nowMoney+drawingMoney;
System.out.println(this.getName()+"取:"+drawingMoney+"万");
System.out.println(account.name+"余额:"+account.money+"万");
System.out.println(this.getName()+"手上:"+nowMoney+"万");
}
}
}
8. 死锁
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
package lock;
public class SiLock {
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
GetCatAndGod t1 = new GetCatAndGod(0,"小明",cat,dog);
GetCatAndGod t2 = new GetCatAndGod(1,"小红",cat,dog);
t1.start();
t2.start();
}
}
class Cat{}
class Dog{}
class GetCatAndGod extends Thread{
int chose;
Cat cat;
Dog dog;
public GetCatAndGod(int chose,String name,Cat cat,Dog dog) {
super(name);
this.chose = chose;
this.cat=cat;
this.dog=dog;
}
@Override
public void run() {
getCatAndGod();
}
public void getCatAndGod(){
if(chose==0){
synchronized (cat){
System.out.println(this.getName()+"拿到猫");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//资源被占用会出现死锁
// synchronized (dog){
// System.out.println(this.getName()+"想拿到狗");
// }
}
synchronized (dog){
System.out.println(this.getName()+"想拿到狗");
}
}else {
synchronized (dog){
System.out.println(this.getName()+"拿到狗");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//资源被占用会出现死锁
// synchronized (cat){
// System.out.println(this.getName()+"想拿到猫");
// }
}
synchronized (cat){
System.out.println(this.getName()+"想拿到猫");
}
}
}
}
9. Lock锁
ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
package lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class RableLock implements Runnable {
private int nums=50;
ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
try{
//加锁
lock.lock();
while (true){
if(nums<=0){
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+(nums--));
}
}finally {
//解锁
lock.unlock();
}
}
public static void main(String[] args) {
RableLock rableLock = new RableLock();
new Thread(rableLock,"学生").start();
new Thread(rableLock,"老师").start();
new Thread(rableLock,"老六").start();
}
}
10. 线程池
- JDK 5.0起提供了线程池相关APl: ExecutorService和Executors
- ExecutorService:真正的线程池接。常见子类ThreadPoolExecutor:
- void execute(Runnable command)∶执行任务/命令,没有返回值,一般用来执行Runnable
- Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
package com.suzezhi.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//测试线程池
public class TestPool implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
TestPool t1 = new TestPool();
TestPool t2 = new TestPool();
TestPool t3 = new TestPool();
//创建一个线程池服务
ExecutorService service = Executors.newFixedThreadPool(10);
//调用Runnable线程
service.execute(t1);
service.execute(t2);
service.execute(t3);
//关闭线程池
service.shutdown();
}
}
Lambda表达式
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑。
必须是函数式接口,就是一个接口只有一个函数的时候,才可以用lambda表达式。
public class Test{
public static void main(String[] args){
Lambda1 l=()->{
System.out.print("这是lambda表达式");
}
l.test();
}
}
interface Lambda1{
void test();
}