1并发与并行:并发:一个cpu交替执行多个任务,有时间差
进程与线程 并行:多个cpu同时执行多个任务
进程:从rom进入到ram中运行的程序叫做进程
线程:打开程序,就会开启一条程序到cpu的路径,cpu可以通过该路径执行该程序
的功能,这条路径就叫做线程。线程属于进程,是进程中的一个执行单元
jvm在执行main方法时,main方法进入到栈内存,jvm会开辟一条main方法到
cpu的执行路径,这个叫做主线程
2线程的调度:分为分时调度和抢占式调度,可以从任务管理器中设置
3创建多线程程序的第一种方法:创建thread类的子类
创建的步骤:1创建thread类的子类,并重写其中的run方法,即,要这个线程具体做什么
2创建自定义thread类的对象
3调用thread类中的start方法每次调用start方法就是开辟一个新的栈空间。
thread.run和thread.start的区别就是每次调用start方法就是开辟一个新的
栈空间后再执行run方法。而run方法只是在main方法执行后再执行。
不会再新建栈空间。
案例1
//对thread类进行重写,创建我们需要的run方法
package com.bed.thread;
public class MyThread extends Thread{
@Override
public void run(){
for(int i=0;i<20;i++){
System.out.println("thread"+i);
}
}
package com.bed.thread;
================================================
public class Mainthread {
public static void main(String[] args) {
//多线程方法
MyThread mt=new MyThread();//新建Mythread类对象
mt.start();//开启新的线程,调用其中我们已经重写了的run方法
for(int i=0;i<20;i++){
System.out.println("main"+i);
}
}
}
运行结果:两个线程随机运行,且发现每次结果不一样,main线程和新的线程一起抢夺CPU的执行权,谁抢到了就执行相应的代码。
注意:
4线程的几种方法:
1获取当前线程的名称
重写run方法
package com.bed.thread;
public class ThreadGetName extends Thread{
@Override
public void run(){
String name=getName();//直接使用getname方法来获取该线程的名称。
System.out.println(name);
Thread t=Thread.currentThread();//获取当前线程
String name1 = t.getName();//获取当前线程名称
}
}
package com.bed.thread;
public class GetName {
public static void main(String[] args) {
ThreadGetName tgn=new ThreadGetName();
tgn.start();
System.out.println(Thread.currentThread().getName());//写在主方法下面来获取主线程 名称
}
}
2 更改线程名称的两种方法:1Thread.setName
2 创建带参数构造方法,方法参数是线程名称,
并把名称教给线程的父类suname)
3Sleep方法:使当前执行线程按照给定毫秒暂停后继续运行
案例 60秒倒计时
public class Sleep {
public static void main(String[] args) {
for (int i = 0; i < 60; i++) {
System.out.println(i);
try {
Thread.sleep(1000);//一千毫秒间隔
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5创建多线程的第二种方法:使用runable实现类
步骤:
重写runable实现类中的run方法
public class Runable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"--"+i);
}
}
}
public class RunableMain {
public static void main(String[] args) {
Runnable r=new Runable();
Thread th=new Thread(r);
th.start();
//主方法进程
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"--"+i);
}
}
}
使用runable接口的便利性:
6使用匿名内部类来创建多线程的方法:
实例
package com.bed.thread;
public class InnerClassThread {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName());
}
}
}.start();
//下面的代码就等于 Runable r=new RunableImpl();+new Thread(r).start;
new Thread(new Runable(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName());
}
}
}).start();
}
}
7线程的安全问题:多个线程在抢夺同个资源的时候会出现的问题
如何避免线程的安全问题:当一个线程在使用共享资源时,无论是否占用CPU。其他线程
都只能等待其完成后再使用CPU。
8如何解决线程安全问题:
1使用Synchronized关键字
通俗的讲,多个线程,只有抢到了这个“锁对象(obj)”,才能执行同步的代码块,
执行完同步代码块就会把锁对象归还,其他线程才可以获取锁所对象,进行后面的
同步代码块运行。但是频繁的获取锁,释放锁会降低程序的效率。
案例
package com.bed.thread;
public class SoldTicket implements Runnable {
private int ticket=100;//一共有一百张票
Object obj=new Object();//创建一个锁对象
@Override
public void run() {
while(true){
synchronized (obj) {//需要锁住的代码部分
if (ticket > 0) {//先判断还有没有票
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票");
ticket--;
}
}
}
}
}
2使用synchronized方法(和使用synchronized关键字一样,就是将共享同步代码装到一个
方法里,然后调用方法即可)
3使用lock锁:
1在成员方法处创建一个lock的实现类对象Lock L1=new Reentrantlock();
2在共享代码前调用lock方法 L1.lock{可能会出安全问题的代码前创建锁}
3在可能出问题的代码后添加L.unlock();释放锁
代码实现
package com.bed.thread;
import java.util.concurrent.locks.ReentrantLock;
public class Locktest implements Runnable {
private int ticket=100;//一共有一百张票
ReentrantLock L1=new ReentrantLock();
@Override
public void run() {
while(true){
L1.lock();//获取锁
if (ticket > 0) {//先判断还有没有票
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
L1.unlock();//释放锁,无论程序是否有问题锁都会释放掉
}
}
}
}
}
9线程执行的6种状态:
10代码实现 等待-唤醒 案例(线程之间的通信)
//实现一个购买的构成,消费者先告知生产者需要什么,然后进行等待。生产者经过一段时间后。
//将生产好的产品交给消费者。
//购买者:告知购买意向,然后调用wait方法进入等待,等待生产者生产。
//生产者:花费五秒钟来制作,做好之后调用notify再告知购买者。
public class WaitCall {
public static void main(String[] args) {
Object obj = new Object();//新建一个锁对象
new Thread() {//消费者模拟
@Override
public void run() {
synchronized (obj){//同步锁保证等待线程与唤醒线程只能有一个运行
System.out.println("我需要买这个那个");
try {
obj.wait();//告知需求后进入睡眠状态
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("谢谢");//在wait结束后进行的代码
}
}
}.start();//启动该线程
new Thread(){//生产者模拟
@Override
public void run() {
synchronized (obj){//此处的锁要与上面的一样
try {//制作时长为五秒
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("制作完毕,您拿好");
obj.notify();//唤醒第一线程
}
}
}.start();
}
}
补充:
唤醒时除了notify方法 还有一个notifyall方法,就是唤醒所有等待线程的意思
11线程间的通信
等待与唤醒机制(线程间的通信):解决多个线程抢夺同一个资源的问题。
案例:买卖包子
待理解
12线程池:就是多个线程的容器,可以用linkedlist进行集合。用Thread t=linked.remove来调用。
调用完后,用linked.add(t)来添加。使用线程池可以提高程序的效率。
使用线程池:
package com.bed;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class threadpool {
public static void main(String[] args) {
//创建一个线程数量为3的线程池
ExecutorService es = Executors.newFixedThreadPool(3);
//将写完的线程开启
es.submit(new runable());
es.submit(new runable());
es.submit(new runable());
}
}