JAVA线程和进程(load.......)

1.进程和线程的关系

进程是多个线程的集合,进程是正在执行的程序,线程是进程中独立运行的子任务,比如正在执行的QQ就是一个进程,里面的好友视频,下载文件,传输数据等等就是独立的子任务,也就是线程。

每一个进程中都有一个主线程,JAVA代码中,main 函数就是一个主线程,多线程可以提高程序的运行效率,任务可以不用排队等待。

1.1 创建线程的方式
1.1.1 继承Thread类的方式

1.在类上继承 Thread
2.在类中重写 Thread的 run 方法
3.在 run 方法里写 线程需要执行的代码
4. 实例化 该类的 对象,调用 start 方法
public class Thread01 {
 /**
  * 什么是进程:进程就是一个正在执行的程序
  * 什么是线程:线程是进程中独立执行的子任务
  */
  public static void main(String[] args) {
  	CreateThread createThread =  new CreateThread();
  createThread.start(); 
 }
}
class CreateThread extends Thread{
 public void run() {
  // TODO Auto-generated method stub
 
  // 买出30张火车票
  for(int i=1;i<=30;i++)
  {
   System.out.println("第"+i+"张票买出");
  }
 }  
}

1.1.2 实现runnable接口方式

1.在类上实现 Runnable 接口
2.重写Runnbale的 run 方法
3.在 run 方法里写 线程需要执行的代码
4. 把该类对象添加到 thread 对象 的构造方法中
5. 调用thread对象的 start 方法
public class Runnable01 {
public static void main(String[] args) {
  // TODO Auto-generated method stub
  Thread t  =  new Thread(new CreateRunnable());
  t.start();
 }
 
}
class CreateRunnable implements Runnable {
public void run() {
  // TODO Auto-generated method stub
  // 买出30张火车票
  for(int i=1;i<=30;i++)
  {
   System.out.println("第"+i+"张票买出");
  }
 }
 
}

注:使用接口方式创建线程比 使用 继承方式更好,因为开发都是面向接口编程,并且继承是单一的,而接口是可以多实现的。

1.1.3 使用匿名内部类方式

1.把 Runnbale 对象 加入 到 Thread对象的构造方法中
2.重写 run 方法
3.调用 thread 的 start 方法
public class Thread02 {
/**
  * 匿名 内部类 创建线程
  * @param args
  */
  public static void main(String[] args) {
  // TODO Auto-generated method stub
  new Thread(new Runnable() {
   
   public void run() {
    // TODO Auto-generated method stub
    // 买出30张火车票
    for(int i=1;i<=30;i++)
    {
     System.out.println("第"+i+"张票买出");
    }
   }
  }).start();
  }
 }

 

1.1.4 使用线程池创建方式

1.2 线程常用的API

1. getID() 获取当前线程的ID ,Runnable和匿名 内部类都没有此方法

2. Thread.sleep(1000) 让当前线程休眠1秒后进入就绪态
放弃线程对CPU一秒钟的资源竞争

3.Thread.currentThread() 获取到当前线程
Thread.currentThread().getID() 获取当前线程的 ID
public class Thread03 {
  
 /**
  * 什么是进程:进程就是一个正在执行的程序
  * 什么是线程:线程是进程中独立执行的子任务
  */
 
 public static void main(String[] args) {
  
  //Thread.currentThread() 和 getID()
  System.out.println(Thread.currentThread().getId());
  CreateThread03 createThread =  new CreateThread03();
  createThread.start(); 
 }
 }
 class CreateThread03 extends Thread{
 //1. getID 获取当前线程的ID ,Runnable和匿名 内部类都没有此方法
 public void run() {
  // TODO Auto-generated method stub
 
  // 买出30张火车票
  for(int i=1;i<=30;i++)
  {
   try {
    
    // Slepp()
    Thread.sleep(1000);
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   //getID()
   System.out.println(getId()+"第"+i+"张票买出");
   }
  }
 }

1.3 守护线程和非守护线程(用户线程)
守护线程是用于服务其他线程的,相当于非守护线程的保姆,垃圾回收器就是一个守护线程。
与主线程的关系
用户线程是主线程 main 创建的线程,但主线程 main 死掉或者执行完毕,不会影响到用户线程。
当主线程 main 死掉或者执行完毕,守护线程和主线程 一起销毁。

把用户线程设置为守护线程方式

  userthread.setDaemon(true);

举列

public class Thread04 {
 public static void main(String[] args){
  
 Thread t = new Thread(new Runnable() {
   
   public void run() {
    // TODO Auto-generated method stub
    for(int i =1 ; i<=30;i++)
    {
     try {
      Thread.sleep(300);
     } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
     System.out.println("子线程ID:"+Thread.currentThread().getId()+"次数:"+i);
    }
   }
  });
  //把该线程t 设置成守护线程,可以发现主线程结束后,守护线程也结束了
  t.setDaemon(true);
  t.start();
  
  for(int i =1 ; i<=5;i++)
  {
   System.out.println("主线程ID:"+Thread.currentThread().getId()+"次数:"+i);
  }
  System.out.println("主线程执行完毕,用户线程进行执行");
  }
 }

1.4多线程运行的状态

新建状态
就绪状态
运行状态
堵塞状态
死亡状态

在这里插入图片描述

1.4.1 Join()方法

Join() 方法的作用:让其他线程编程阻塞状态

B.join(); 
如:添加在A线程中,A变成阻塞状态,直到B线程执行完毕和释放,A才执行。

2.多线程实现同步和安全

2.1 什么是线程安全

当多个线程共享同一个全局变量时,在对该变量进行写操作时,可能会产生数据冲突和不确定问题,也就是线程安全问题,读操作不会发生线程安全问题,因为归根到底读操作是受到写操作影响。

2.2多线程同步方法

2.2.1 使用synchronized锁

同步代码块方式,用synchronized包裹共享代码块,且使用同一把锁。
public class Thread04 {
 public static void main(String[] args){
  CreaterThreadx createrThreadx  = new CreaterThreadx();
  //t1 t2 两个线程同时对 tk变量进行操作。
  Thread t1 = new Thread(createrThreadx);
  Thread t2 = new Thread(createrThreadx);
  t1.start();
  t2.start();
 }
}
class CreaterThreadx implements Runnable{
// tk为共享变量
    private int tk = 100;
    Object object  = new Object();
 public void run() {
  // TODO Auto-generated method stub
  while(tk>0)
  {
   sale();
  }
 }
 public void sale(){
  //同步代码块方式
  synchronized (object) {
   if(tk>0)
   {
    System.out.println("线程"+Thread.currentThread().getId()+"出售第"+(100-tk+1));
    tk--;
   }
  }
 }
}

当同步代码块执行完毕或者抛出异常,则当前线程就会释放锁。

同步函数方式 在方法上加上synchronized,使用的this锁
public class Thread04 {
 public static void main(String[] args){
  CreaterThreadx createrThreadx  = new CreaterThreadx();
  //t1 t2 两个线程同时对 tk变量进行操作。
  Thread t1 = new Thread(createrThreadx);
  Thread t2 = new Thread(createrThreadx);
  t1.start();
  t2.start();
 }
}
class CreaterThreadx implements Runnable{
// tk为共享变量
    private int tk = 100;
    Object object  = new Object();
 public void run() {
  // TODO Auto-generated method stub
  while(tk>0)
  {
   sale();
  }
 
 }
  public synchronized void sale(){
  //同步代码块方式
 /* synchronized (object) {*/
   if(tk>0)
   {
    System.out.println("线程"+Thread.currentThread().getId()+"出售第"+(100-tk+1));
    tk--;
   }
  /*}*/
 }
}	
静态同步函数 不是使用this锁,采用 .class文件,当前字节码文件。

2.2.2 多线程死锁

一般来说都是 锁中嵌套锁,导致锁获取不到,资源也释放不了。

public class Thread05 {
/**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  final Object object1 = new Object();
  final Object object2 = new Object();
  new Thread(new Runnable() {
   
   public void run() {
    // TODO Auto-generated method stub
    synchronized (object1) {
     System.out.println("o1上锁,等待o2");
     try {
      Thread.sleep(1000);
     } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
     synchronized (object2) {
      System.out.println("t1执行完毕");
     }
    }
   }
  }).start();
  new Thread(new Runnable() {
   
   public void run() {
    // TODO Auto-generated method stub
    synchronized (object2) {
     System.out.println("o2上锁,等待o1");
     synchronized (object1) {
      System.out.println("t2执行完毕");
     }
    }
   }
  }).start();
  }
 }

3.JAVA内存模型(多线程的关系)

多线程三大特性:原子性,有序性,可见性

原子性: 代码块要不不执行,要不一起执行。原子操作。
有序性:代码块按照从上到下顺序执行。
可见性:共享变量的变化,可以被其他方法获取到。

JAVA内存模型由 主内存(存放共享全局变量)和本地内存(存放私有变量)组成。

本地内存会复制主内存的变量到本地内存中,线程会对本地内存的共享变量
进行写操作,然后更新到主内存中,然而其他本地内存中的共享变量没有与主
内存一起更新,就导致其他线程使用的还是以前的共享变量,这就是可见性问题。

Volatile 解决可见性问题

 Volatile 可以解决可见性问题,但不能解决原子性问题

AtomicInteger 解决原子性问题

在JDK1.5的Atomic 包下 ,可以保证int String boolean 等类型的原子性操作。

用法如下:

public class Thread06{
public static void main(String[] args) {
  // TODO Auto-generated method stub
  ThreadAtomic[] ThreadAtomic = new ThreadAtomic[10];
  for(int i = 0 ;i< ThreadAtomic.length;i++)
  {
   ThreadAtomic[i] = new ThreadAtomic();
  }
  for(int i = 0 ;i< ThreadAtomic.length;i++)
  {
   ThreadAtomic[i].start();
  }
 }
} 
class ThreadAtomic extends Thread {
//private static int count = 0; 相当于以下。
 private static AtomicInteger count = new AtomicInteger(0);
 
 public void run() {
  for(int i=0;i<1000;i++)
  {
   //count++;
   count.incrementAndGet();
  }
  //输出count
  System.out.println(getId()+"次数"+count.get());
  }
 }

4.多线程通讯

多线程通讯就是 多个线程操作同一个资源,但是操作的动作不同。

案列操作:
一个共享资源:User对象
一个生产者线程:InputThread
一个消费者线程:Outinpu tThread
生产者线程进行写操作,消费者线程进行读操作。

public class User {
	private String name;
 private String Sex;
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getSex() {
  return Sex;
 }
 public void setSex(String sex) {
  Sex = sex;
 }
 }

生产者线程

public class InputThread extends Thread {
 User user;
 public InputThread(User user){
  this.user= user;
 }
 public void run() {
  // TODO Auto-generated method stub
  int count = 0;
  
  while(true)
  {
   count=(count+1)%2;
   //count是偶数时就执行这个
   if(count ==0)
   {
    user.setName("小明");
    user.setSex("男");
   }
   else
   {
    user.setName("小红");
    user.setSex("女");
   }
  }
 }
 }

消费者线程

public class OutinputThread extends Thread {
  User user;
  public OutinputThread(User user){
   this.user= user;
  }
  public void run() {
  while(true)
  {
   System.out.println(user.getName()+","+user.getSex());
  }
 }
}

测试类

public class Test {
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  User user = new User();
  InputThread inputThread = new InputThread(user);
  OutinputThread outinputThread = new OutinputThread(user);
 }
}

结果:
在这里插入图片描述
出现了线程不安全,结果不确定现象。
原因分析: 生产者线程和消费者线程出现了非原子性的操作,生产者线程的代码块还没有操作完毕,消费者线程就开始进行了读取操作,所以导致了结果不确定现象。

解决办法:
1.给共享资源加锁,保证同步读写(略)。

2.采用wait和notify方法 ,只能在synchronized中使用。
wait()方法

释放掉当前线程所持有的锁
让当前线程从运行状态,变成阻塞状态

notify()方法

获取当前线程所持有的锁
让当前线程从阻塞状态,变成运行状态(不是就绪态)

以上两个方法在多个线程同步的时候才能使用。

生产者线程

public class InputThread extends Thread {
 User user;
 int count = 0;
 public InputThread(User user){
  this.user= user;
 }
 public void run() {
  // TODO Auto-generated method stub
  while(true)
  {
   count=(count+1)%2;
   //count是偶数时就执行这个
   synchronized (user) {
    if(Test.flag==1)
    {
     try {
      //让当前线程变成阻塞态。并释放掉user这个锁资源
      user.wait();
     } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
    if(count ==0)
    {
     user.setName("小明");
     user.setSex("男");
    }
    else
    {
     user.setName("小红");
     user.setSex("女");
    }
    Test.flag=1;
    //让当前休眠的线程变成运行态(不是就绪),并获取user这个锁资源    user
    user.notify();
   } 
  } 
 }
}

消费者线程

public class OutinputThread extends Thread {
  User user;
  public OutinputThread(User user){
   this.user= user;
  }
  public void run() {
  while(true)
  {
   synchronized (user) {
    if(Test.flag==0)
    {
     try {
      //让当前线程变成阻塞态。并释放掉user这个锁资源
      user.wait();
     } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
   System.out.println(user.getName()+","+user.getSex()); 
   Test.flag=0;
   //让当前休眠的线程变成运行态(不是就绪),并获取user这个锁资源
   user.notify();
   }
  }
 }
}

测试类

public class Test {
 static int flag =0;
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  User user = new User();
  InputThread inputThread = new InputThread(user);
  OutinputThread outinputThread = new OutinputThread(user);
  inputThread.start();
  outinputThread.start();
 }
}

结果如下:
在这里插入图片描述

可以发现,解决了线程不安全的现象 1.生产者和消费者同步了,2.解决了原子性问题。

4.1 Lock锁

是一把可以自定义开始和结束的锁

Lock和synchronized的区别
1.synchronized发生异常时会自动释放锁,Lock在发生异常时,必须把unLock()方法加入到finally中
2.Lock可以让正在等待获取锁的线程中断,让此线程可以干别的事,而synchronized不能释放正在等待锁资源的线程。
3.Lock锁可以以自旋的方式去获取锁,是非阻塞的方式,synchronized是阻塞的方式

Lock的 await() 方法

相当于 synchronized的 wait()方法
lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
conditon.await();

Lock的 signal() 方法

相当于synchronized的 notify() 方法
lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
conditon.signal();

Lock 的上锁和 解锁方式

 注:一定要配备try catch finally使用,否则当异常时无法释放锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Thread07 {
    public static void main(String[] args) {
        ThreadLock threadLock = new ThreadLock();
        Thread t1  = new Thread(threadLock);
        Thread t2 = new Thread(threadLock);
        t1.start();
        t2.start();
    }
}
class ThreadLock implements Runnable
{
    Lock lock = new ReentrantLock();
    int count =  30;
    public void run() {
        while(count>0)
        {
            //LOCK锁要配和try catch finally使用,否则出现异常,锁不能释放
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName()+"票数:"+count);
                count--;
            }
            catch (Exception e){
            }
            finally {
                lock.unlock();
            }
        }
    }
}

ThreadLocal 本地线程

把主内存的共享变量复制下来,为每一个线程提供一个局部变量,
每一个线程都可以独立的改变自己的变量,而不会影响到其他线程的变量。

Vector和ArraryList的区别

Vector是线程安全的,效率不高,方法上加了synchronized锁
ArraryList是线程不安全的,效率高

HashTable,HashMap,concurrentHashMap的区别

HashTable 是线程安全的,效率最低,方法上加了synchronized锁
HashMap不是线程安全的,但效率最高
conrurentHashMap是线程安全的,效率比HashTable高,采用分段锁

conrurentHashMap将一个整体拆分成多个小的HashTable,默认分成16段。

在这里插入图片描述

并发队列

	都继承 Queue接口

ConcurrentLinkedQueue 高性能队列
底层采用链表的结构实现,是一种无界安全队列,性能好于BlockingQueue,不会出现队列阻塞的情况,因为链表可以无限开辟。

BlockingQueue 阻塞队列
当队列满的时候,就会变成阻塞状态,是一种有界安全队列。

使用ConcurrentLinkedQueue 的例子

实现生产者生成一个数据,消费者就获取一个数据
public class Thread08 {
 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  ConcurrentLinkedQueue<String> concurrentLinkedQueue = new ConcurrentLinkedQueue<String>();
  new ProdectorThread(concurrentLinkedQueue).start();
  new ConsumerThread(concurrentLinkedQueue).start();
 }
}
class ProdectorThread extends Thread
{
 // 创建队列
 private ConcurrentLinkedQueue concurrentLinkedQueue;
 //创建标志位
 private volatile Boolean flag= true;
 //创建atomic 的全局静态变量,保证原子性
 private static AtomicInteger count = new AtomicInteger();
 public ProdectorThread(ConcurrentLinkedQueue concurrentLinkedQueue){
  this.concurrentLinkedQueue=concurrentLinkedQueue;
 }
 @Override
 public void run() {
  System.out.println("生产者线程启动。。。。。。");
  try {
   while(flag)
   {
    //这里sleep的作用是,让消费者先执行,进入wait状态。
    Thread.sleep(1000);
    synchronized (concurrentLinkedQueue) {
     System.out.println("正在添加队列");
     //数据自增
     String date = count.incrementAndGet()+"";
     //数据入队列
     concurrentLinkedQueue.offer(date);
     System.out.println("添加队列成功");
     concurrentLinkedQueue.notify();
    }
   }
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  finally{
   System.out.println("生产者停止。。。。");
  }
 }
 public void stopThread(){
  this.flag = false;
 }
}
class ConsumerThread extends Thread{
 // 创建队列
 private ConcurrentLinkedQueue concurrentLinkedQueue;
 //创建标志位
 private volatile Boolean flag= true;
 public ConsumerThread(ConcurrentLinkedQueue concurrentLinkedQueue)
 {
  this.concurrentLinkedQueue=concurrentLinkedQueue;
 }
 @Override
 public void run() {
  System.out.println("消费者线程启动");
  try {
   while(flag)
   {
    synchronized (concurrentLinkedQueue) {
     //出队列
     String date=(String) concurrentLinkedQueue.poll();
     if(date!=null)
     {
      System.out.println("消费者获取date"+date);
     }
     else
     {
      concurrentLinkedQueue.wait(); 
     }
    }
   }  
  } catch (Exception e) {
   // TODO: handle exception
  }finally{
   System.out.println("消费者停止");
  }
 }
}

线程池

一个项目中经常创建,启动,销毁线程非常消耗资源和时间的,线程池可以对线程进行复用和管理。

线程池可以复用线程,从而做到节约资源和时间,也方便对线程进行管理。

线程池核心使用的方法是 ThreadPoolExecutor,接口是Executors

Executors提供创建4种线程池的方法
1.newCachedThreadPool方法是一个可缓存线程池,如果线程池长度超过处理需要,则可以灵活的回收空闲线程
2.newFixedThreadPool方法 是一个定长线程池,可以控制线程的最大并发输,超出的线程将会在队列中等待
3.newScheduledThreadPool方法 是一个定长线程池,支持定时和周期性任务
4.newSingleThreadPool方法是一个单线程化的线程池,保证任务按照顺序执行

newCachedThreadPool的使用

public class Thread09 {
 public static void main(String[] args){
  //1.创建可缓存的线程池
  ExecutorService newcached= Executors.newCachedThreadPool(); 
  //2.通过线程池创建9个线程
  for(int i = 0; i<9;i++)
  {
   final int temp=i;
   //3.调用execute方法,实现Runnable接口创建线程。
   newcached.execute(new Runnable() {
   public void run() {
    // TODO Auto-generated method stub
    System.out.println("线程名称"+Thread.currentThread().getName()+"  i:"+temp);
   }
   });
  }
 }
}

结果:

在这里插入图片描述

可以发现线程池pool-1,没有创建9个线程,只创建了6个线程
证明了线程池已经对线程进行了复用,从而提高了效率

newFixedThreadPool的使用

public class Thread10 {
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  //1.可固定长度的线程池,最多有4个线程
  ExecutorService newFixed= Executors.newFixedThreadPool(4);
  for(int i=0;i<10;i++)
  {
   final int temp=i;
   newFixed.execute(new Runnable() {
    public void run() {
     // TODO Auto-generated method stub
     System.out.println("线程名称"+Thread.currentThread().getName()+"  i:"+temp); 
    }
   });
  } 
 }
}

总结:

在这里插入图片描述

可以发现10次new Runnable,但一共只创建了规定的4个线程,
实现了线程池的固定长度和线程池的复用。

newScheduledThreadPool的使用

newScheduledThreadPool主要采用schedule方法进行线程定时的设置,规定线程在规定的时间内启动。

如规定线程在3秒以后全部启动。

public class Thread11 {
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  //1.可定时的线程池,设置线程池最多有4个线程。
  ScheduledExecutorService newSched= Executors.newScheduledThreadPool(4);
  for(int i=0;i<10;i++)
  {
   final int temp=i;
   //2.采用schedle方法进行定时设置,表示3秒后线程全部开始启动
   newSched.schedule(new Runnable() {
    public void run() {
     // TODO Auto-generated method stub
     System.out.println("线程名称"+Thread.currentThread().getName()+"  i:"+temp); 
    }
   },3,TimeUnit.SECONDS);
  } 
 }
}

总结:
在这里插入图片描述

可以发现该线程池也可以设置线程池的最大线程数
该线程池也可以设置线程启动的时间和周期

shutdown 方法 停止线程池。

线程池原理

线程池都是采用的 new ThreadPoolExector() 构造函数创建的。

核心线程池是指初始化时线程池的数量。
最大线程池是指线程池中线程最大的数量。

ThreadPoolExector的参数
1.CorePoolSize 设置核心池大小
2.MaximumPoolSize 设置线程池最大线程数
3.KeepAliveTime 设置线程存活的时间
4.Unit 设置存活时间的单位

具体流程

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值