Java线程

程序-进程-线程
程序的概念:为了完成特定的操作,使用计算机语言编写一系列代码指令集合,也称静态代码。

进程的概念:在运行中的程序,被加载到内存中,是操作系统分配资源最小的单位。

线程的概念:进程细化为线程,是进程中的最小执行单元(任务),是操作系统进行任务调度的最小单位。线程隶属于进程。

线程和进程的关系
一个进程可以包含多个线程,但一个线程只能属于一个进程,线程不能离开进程单独运行。

每个进程至少包含一个线程,即主线程;在主线程开始执行程序,java程序的入口main()方法就是在主线程中被执行的。

在主线程中可以创建和启动其他线程,一个进程内所有线程共享该进程的内存资源。

package Day1.Demo1;
 
public class Demo1 {
    //java中mian方法是用来启动主线程的
 
    public static void main(String[] args) {
        //单线程模式
        System.out.println("mian开始");
        Demo1 demo1=new Demo1();
        demo1.good();
        System.out.println("mian结束");
    }
    public void good(){
        System.out.println("good");
    }
}


 

创建线程
在Java中,要实现线程,可以继承Thread的类或实现Runnable接口。

Thread类,线程的方法存放在类中。
Runnable接口,线程的方法存放在接口中。
建议使用Runnable接口,防止继承Thread后不能继承其他类(避免了单继承的局限性)。

package Day1.Demo1;
 
public class MyThread extends Thread{
    /*
    自定义的线程:继承Thread类,Thread类提供了对线程管理的方法
     */
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("Thread:"+i);
        }
    }
}
 
package Day1.Demo1;
 
public class Demo2 {
 
    public static void main(String[] args) {
        //新建线程
        MyThread myThread=new MyThread();
        Thread thread=new Thread(myThread);
        //thread.run();//run()和单线程效果一样,只是调用线程的方法,不是启动线程
        thread.start();//启动线程
 
        for (int i = 0; i < 100; i++) {
            System.out.println("main:"+i);
        }
    }
}

package Day1.Demo2;
/*
Thread类,线程的方法存放在类中
Runnable接口,线程的方法存放在接口中
建议使用Runnable接口,防止继承Thread后不能继承其他类(避免了单继承的局限性)
 */
 
public class MyThread implements Runnable{
    /*
    自定义线程:实现了Runnable接口,Runnable提供了对线程管理的方法
     */
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
 
package Day1.Demo2;
 
public class Demo1 {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        //线程1
        Thread thread1=new Thread(myThread);
        thread1.start();
        //线程2
        Thread thread2=new Thread(myThread);
        thread2.start();
 
    }
}

Thread类中方法
Thread()创建线程
Thread(target)创建线程,并指定任务
Thread(target,name)创建线程,并指定任务,给这个线程命名
start()启动线程
run()调用线程的方法
getName()获得线程名字
setName()设置线程名字
getPriority()获得线程的优先级
setPriority()设置线程的优先级
currentThread()返回对当前正在执行的线程对象的引用
getId()返回线程的标识符
join()加入线程,先将加入的线程执行完在执行原有线程
sleep(long millis)线程休眠,休眠millis时间
yield()线程让步,让出cup,主动停止执行,等待其他线程完成在执行
MAX_PRIORITY线程优先级的最大值(10)
MIN_PRIORITY线程优先级的最小值(1)
NORM_PRIORITY线程优先级的默认值(5)

package Day1.Demo3;
 
public class Test1 {
    /*
    Thread()创建线程
    Thread(target)创建线程,并指定任务
    Thread(target,name)创建线程,并指定任务,给这个线程命名
    start()启动线程
    run()调用线程的方法
    getName()获得线程名字
    setName()设置线程名字
    getPriority()获得线程的优先级
    setPriority()设置线程的优先级
    currentThread()返回对当前正在执行的线程对象的引用
    getId()返回线程的标识符
    join()加入线程,先将加入的线程执行完在执行原有线程
    sleep(long millis)线程休眠,休眠millis时间
    yield()线程让步,让出cup,主动停止执行,等待其他线程完成在执行
    MAX_PRIORITY线程优先级的最大值(10)
    MIN_PRIORITY线程优先级的最小值(1)
    NORM_PRIORITY线程优先级的默认值(5)
     */
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread, "main");
        thread.start();
 
        Thread thread1=new Thread(myThread,"新加入的线程");
        thread.setName("main的主方法");//设置线程名字
        thread.join();//等待该线程结束
        thread1.start();
 
        System.out.println(thread.getPriority());
        System.out.println(thread.MAX_PRIORITY);
        System.out.println(thread.MIN_PRIORITY);
        System.out.println(thread.NORM_PRIORITY);
    }
}
 
package Day1.Demo3;
 
public class MyThread implements Runnable{
 
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(100);//线程休眠
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

线程优先级
getPriority()获得线程的优先级

setPriority()设置线程的优先级,设置完优先级后,不一定优先级高的每次都先执行,需要看系统的调用。

MAX_PRIORITY线程优先级的最大值(10)
MIN_PRIORITY线程优先级的最小值(1)
NORM_PRIORITY线程优先级的默认值(5)
线程状态 
线程从创建到死亡共有五状态,他们分别是:

新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对 象处于新建状态

就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源

运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能

阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态

死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束

守护线程
Java中的线程分为两类:用户线程和守护线程

任何一个守护线程都是整个JVM中所有非守护线程的保姆,只要当前JVM实例中尚存在

任何一个非守护线程没有结束,守护线程就全部工作;

只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。

守护线程的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC

(垃圾回收器),它就是一个很称职的守护者。

用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了.

设置守护线程: setDaemon(boolean on)

注意:设置线程为守护线程必须在启动线程之前,否则会跑出一个IllegalThreadStateException异常。

 多线程的概念
多线程的概念就是同一时间,一个程序内部多个任务同时执行。也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

多线程的优点:提高了CPU的利用率,提高了程序的处理能力,响应速度也提高了,压榨硬件的价值,提升程序结构。

多线程的缺点:对CPU、内存的配置要求提高了,所以需要对硬件性能进行改善;同时因为多线程需要携程和管理,需要cpu时间来跟踪线程。

线程安全问题:多线程和访问同一个资源。

线程同步
当执行多线程时,不同线程会访问同一资源,这是会出现争抢的问题,出现线程安全问题。这时候就需要线程同步机制,加入锁,让线程排队。

并发:在一段时间内执行执行操作任务。

并行:多个CPU同时进行多项任务

synchronized关键字
synchronized关键字:同步锁,用来修饰代码块,方法。

同步锁:同步锁可以是任何对象,必须唯一,保证多个线程获得是同一个对象(用来充当锁标记)。

同步执行过程:

第一个线程访问,锁定同步对象,执行其中代码。
第二个线程访问,发现同步对象被锁定,无法访问。
第一个线程访问完毕,解锁同步对象。
第二个线程访问,发现同步对象没有锁,然后锁定并访问。
锁的缺点:一个线程持有锁会导致其他所有需要此锁的线程挂起;在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题。

package Day2.Demo1;
 
public class TicketThread extends Thread {
    static int number = 20;
    static Object object = new Object();
 
    @Override
    public void run() {
        while (true) {
            synchronized (object) {
                if (number > 0) {
                    System.out.println(Thread.currentThread().getName() + "买了第" + number + "张票");
                    number--;
                } else {
                    break;
                }
            }
        }
    }
}
package Day2.Demo1;
 
public class Ticket {
    public static void main(String[] args) {
        TicketThread ticketThread = new TicketThread();
        Thread thread = new Thread(ticketThread,"杨文财");
        Thread thread1=new Thread(ticketThread,"贾浩鑫");
        thread.start();
        thread1.start();
    }
}

package Day2.Demo2;
 
public class PrintThreat implements Runnable{
    int number=10;
 
    @Override
    public void run() {
        try {
            print();
 
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
 
 
    public synchronized void print() throws InterruptedException {
        while (true) {
        if (number>0) {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "买了第" + number + "张票");
            number--;
        }else {
        break;
        }
    }
}
}
package Day2.Demo2;
 
public class Ticket {
    public static void main(String[] args) {
        PrintThreat ticketThread = new PrintThreat();
        Thread thread1 = new Thread(ticketThread, "杨文财");
        thread1.start();
        Thread thread2 = new Thread(ticketThread, "白茗宇");
        thread2.start();
    }
}

Lock(锁)
从JDK 5.0开始,Java提供了更强大的线程同步机制-通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。

ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。

synchronized和Lock锁的区别:

synchronized:

可以修饰代码块和方法。
自动添加同步锁,是隐式锁,当同步代码块执行完或出现异常时,锁会自动释放。
是一个关键字,是依靠依靠底层编译后的指令来控制实现的。
ReentrantLock锁:

只能修饰代码块。
需要手动添加和手动释放。
是java.util.concurrent.locks包下的一个类,是依靠java代码来实现控制。

package Day2.Demo3;
 
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class TicketThread extends Thread {
    static int number = 20;
    static Object object = new Object();
    Lock lock=new ReentrantLock();
 
    @Override
    public void run() {
        while (true) {
            lock.lock();//获取锁
                if (number > 0) {
                    System.out.println(Thread.currentThread().getName() + "买了第" + number + "张票");
                    number--;
                } else {
                    break;
                }
            lock.unlock();//释放锁
        }
    }
}
 
package Day2.Demo3;
 
public class Ticket {
    public static void main(String[] args) {
        TicketThread ticketThread = new TicketThread();
        Thread thread = new Thread(ticketThread,"杨文财");
        Thread thread1=new Thread(ticketThread,"贾浩鑫");
        thread.start();
        thread1.start();
    }
}

线程死锁
多线程执行时,会相互占用对方线程所需的资源,当资源被占用时,会等待对方释放资源,所以设计程序时需考虑清楚锁的顺序,尽量减少锁的嵌套。

出现死锁时,不会有异常提示,所有线程处于阻塞状态,无法继续向下执行。

下面是一个死锁的例子:



package Day3.Demo1;
 
public class Test {
    /*
    多线程执行时,会相互占用对方线程所需的资源,当资源被占用时,会等待对方释放资源,
    所以设计程序时需考虑清楚锁的顺序,尽量减少锁的嵌套
     */
    public static void main(String[] args) {
        new DieLockThread(true).start();
        new DieLockThread(false).start();
    }
}
package Day3.Demo1;
 
public class DieLockThread extends Thread {
    //死锁
    boolean flag;
    static Object obj1 = new Object();
    static Object obj2 = new Object();
 
    public DieLockThread(boolean flag) {
        this.flag = flag;
    }
 
    @Override
    public void run() {
        if (flag == true) {
            synchronized (obj1) {
                System.out.println("if obj1");
                synchronized (obj2) {
                    System.out.println("if obj2");
                }
            }
        } else {
            synchronized (obj2) {
                System.out.println("else obj2");
                synchronized (obj1) {
                    System.out.println("else obj1");
 
                }
            }
        }
    }
}

线程通信
线程通信是指多个线程之间相互调度,相互牵制,即线程之间相互作用。

线程通信设计一下三个方法:

.notify():唤醒等待的线程。
.notifyAll():唤醒所有等待的线程。
.wait():执行该方法时,当前线程会进入阻塞状态,释放锁,必须使用锁对象来调用wait()(和sleep()对比有差别)。
以上三个方法必须在同步代码块或同步方法中使用。

下面是两个精度案例:

1、两个线程交替打印1-100之间的数字 

package Day3.Demo4;
 
public class PrintNumber implements Runnable {
    /*
    wait()、notify()、notifyAll() 在代码块中使用
     */
    int number;
 
    @Override
    public void run() {
        while (number<100){
            synchronized (this){
            this.notify();//唤醒等待的线程
                //this.notifyAll();//唤醒所有等待的线程
                System.out.println(Thread.currentThread().getName()+":"+number);
                number++;
            try {
                this.wait();//执行该方法时,当前线程会进入阻塞状态,释放锁,必须使用锁对象来调用wait()(和sleep()对比有差别)
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
            }
        }
    }
 
package Day3.Demo4;
 
public class Test {
    public static void main(String[] args) {
        PrintNumber printNumber=new PrintNumber();
        Thread thread1=new Thread(printNumber,"老王");
        Thread thread2=new Thread(printNumber,"老李");
        thread1.start();
        thread2.start();
    }
}

2、生产者/消费者问题

生产者(Productor)将产品放在柜台(Counter),而消费者(Customer)从柜台处取走产品,生产者一次只能生产固定数量的产品(比如:1),这时柜台中不能再放产品,此时生产者应停止生产等待消费者拿走产品,此时生产者唤醒消费者来取走产品,消费者拿走产品后,唤醒生产者,消费者开始等待.

package Day3.Demo3;
 
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class Counter {
    int number=0;
 
    public synchronized void add() throws InterruptedException {
        if (number==0) {
            this.number = 1;
            System.out.println("生产者增加了一件商品");
            this.notify();
        }else {
            this.wait();
        }
    }
 
    public synchronized void sub() throws InterruptedException {
        if (number==1){
            this.number=0;
            System.out.println("消费者购买 一件商品");
            this.notify();
        }else {
            this.wait();
        }
    }
}
package Day3.Demo3;
 
public class Customer implements Runnable{
    Counter cunter;
 
    public Customer(Counter cunter) {
        this.cunter = cunter;
    }
 
    @Override
    public void run() {
        while (true){
            try {
                cunter.sub();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
package Day3.Demo3;
 
public class Productor implements Runnable{
    Counter cunter;
 
    public Productor(Counter cunter) {
        this.cunter = cunter;
    }
 
    @Override
    public void run() {
        while (true){
            try {
                cunter.add();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
package Day3.Demo3;
 
 
public class Test {
    public static void main(String[] args) {
        Counter cunter=new Counter();
        Customer customs=new Customer(cunter);
        Productor protector=new Productor(cunter);
        Thread thread1=new Thread(customs);
        Thread thread2=new Thread(protector);
        thread1.start();
        thread2.start();
    }
}

新增创建线程方式--Callable接口
Callable接口:与Runnable接口相较,Callable接口的功能更强大。

优点有:1、方法可以抛出异常;2、方法run()可以由返回值;3、需要借助FutureTask类,获取返回结果。

接收任务:FutureTask<T> futureTask = new FutureTask(任务);

package Day3.Demo2;
 
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
 
public class TestCallable {
    public static void main(String[] args) {
        MyCallable myCallable=new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask(myCallable);
        Thread t = new Thread(futureTask);
        t.start();
        try {
            System.out.println(futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
 
    }
}
package Day3.Demo2;
 
import java.util.concurrent.Callable;
 
public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int n=0;
        for (int i = 0; i < 100; i++) {
            n+=4;
        }
        return n;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值