并行和并发
并行:同时执行
并发:交替执行
进程和线程
进程:正在执行的程序
线程:程序的执行路径,一个程序中可以包含多条执行路径(多线程程序)
举例:飞秋、迅雷、360安全管家
线程的实现方式
继承Thread方式
实现步骤:
1.写一个子类继承Thread
2.复写run方法(线程的执行代码)
3.创建Thread的子类对象
4.调用start方法,开启线程
public class DownloadThread extends Thread{
@Overried
public void run(){
for(int i=0;i<=100;i++){
System.out.println("正在下载..."+i+"%");
}
}
}
public class ThreadDemo1{
public static void main(Stirng[] args){
//3.创建Thread的子类对象
DownloadThread dt1=new DownloadThread();
dt1.start();
DownloadThread dt2=new DownloadThread();
d2t.start();
}
}
实现Runnable方式
实现步骤:
1.写一个子类实现Runnable接口
2.复写run方法(线程的执行代码)
3.创建Thread对象,把Runnable对象当做参数传递给Thread
4.调用start方法,开启线程
public class DownloadRunnable implements Runnable{
//run方法中是线程的执行代码
@Override
public void run(){
for(int i=0;i<=100;i++){
//先获取当前正在执行的线程Thread对象,再通过Thread对象获取线程的名称
String name= Thread.currentThread().getName();
System.out.println(name+"正在下载..."+i+"%");
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
//创建Runnable的实现类对象
DownloadRunnable dr=new DownloadRunnable();
//创建线程对象
Thread t1=new Thread(dr);
t1.start();
//创建线程对象
Thread t2=new Thread(dr);
t2.start();
}
}
实现Callable方式
实现步骤:
1.写一个实现类实现Callable
2.复写call方法(线程的执行代码,有返回值)
3.创建Callable的实现对象,传递给FutureTask
4.创建Thread对象,接受FutureTask对象
5.调用start方法,开启线程
6.调对象FutureTask的get方法,获取先的执行结果。
public class DownloadCallable implements Callable<String> {
//run方法中是线程的执行代码
@Override
public String call(){
for(int i=0;i<=100;i++){
//先获取当前正在执行的线程Thread对象,再通过Thread对象获取线程的名称
String name= Thread.currentThread().getName();
System.out.println(name+"正在下载..."+i+"%");
}
return "完毕";
}
}
public class ThreadDemo3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建Runnable的实现类对象
DownloadCallable dr=new DownloadCallable();
//创建FutrueTack对象
FutureTask<String> task1=new FutureTask<String>(dr);
//创建线程对象
Thread t1=new Thread(task1);
t1.start();
//创建FutrueTack对象
FutureTask<String> task2=new FutureTask<String>(dr);
//创建线程对象
Thread t2=new Thread(task2);
t2.start();
//获取执行结果
String s1 = task1.get();
String s2 = task2.get();
System.out.println(s1);
System.out.println(s2);
}
}
三种实现方式的区别
1.实现Runable方式:实现接口的同时,也可以继承其他类。
2.继承Thread方法:由于Java是单继承,不能再去继承其他类。
3.实现Callalble方式:有回值
Thread类的方法
public static void sleep(long time)
让线程休眠指定的毫秒值
public void setPriority(int number)
设置线程优先级的方法(1~10优先级越高,执行到的几率越高)
public void setDaemon(boolean true)
设置线程为守护线程。当普通线程执行完毕后,守护线程也会根这结束。
线程安全问题
产生的原因:
多个线程在访问共享数据时,可能一个线程还没有执行完,执行权就被另一个线程抢走了,就可能有线程安全问题
解决方案
1.同步代码块
//任意对象,要保证唯一
synchronized(锁对象){
有安全问题的代码
}
2.同步方法
//同步方法的锁对象是: this
public synchronized void method(){
}
//静态的同步方法锁对象是:字节码对象
public synchronized static void method(){
}
3.Lock锁
//创建锁对象
Lock lock=new ReentrantLock();
//上锁
lock.lock();
...有线程安全问题的代码...
//解锁
lock.unlokc();
死锁问题
死锁的问题是因为同步代码块不合理的嵌套导致的,两个线程相互持有多方的锁,导致两个线程都不能执行。
生产者和消费者
生产者和消费者是一个很经典的解决供需关系的一个案例。
生产者(线程)
判断是否已经有数据
有:等待
没有:生产产品,把消费者唤醒,把产品的状态改成有
消费者(线程)
判断是否已经有数据
没有:等待
有:消费这个产品,把产品状态改为没有,唤醒生产者
产品(共享数据)
产品的状态
- 共享数据
public class Desk{
private boolean flag; //数据的状态
private final Object lock=new Object(); //锁对象
private int count=10; //控制线程执行次数
//构造方法,get和set方法,自己快捷键生成
}
- 生产者线程
//厨师
public class Cooker extends Thread{
private Desk desk;
//使用构造方法给成员变量赋值
private Cooker(Desk desk){
this.desk=desk;
}
@Override
public void run(){
while(true){
if(deskc.getCount()==0){
break;
}else{
synchronized(desk.getLock()){
//判断是否已经有数据(汉堡包)
if(desk.isFlag()){
//有:等待
desk.getLock().wait();
}else{
//没有:生产产品,把产品的状态改成有(true),把消费者唤醒,
System.out.println("厨师正在生产汉堡包");
desk.setFlag(true);
desk.getLock().notifyAll();
}
}
}
}
}
}
- 消费者线程
public class ChiHuo extends Thread{
private Desk desk;
//使用构造方法给成员变量赋值
private ChiHuo(Desk desk){
this.desk=desk;
}
@Override
public void run(){
while(true){
if(desk.getCount()==0){
break;
}else{
synchronized(desk.getLock()){
//判断是否已经有数据(汉堡包)
if(desk.getFlag()){
//有:消费这个产品,把产品状态改为没有(false),唤醒生产者线程
System.out.println("吃货开始吃汉堡包");
desk.setCount(desk.getCount()-1);
desk.setFlag(false);
desk.getLock().nofityAll();
}else{
//没有:等待
desk.getLock().wait();
}
}
}
}
}
}