Advertisement

多线程相关知识(Java)

阅读量:

一、基本知识

1、进程:正在运行的程序,是系统进行资源分配的基本单位;目前操作系统都是支持多进程,可以同时执行多个进程,通过进程ID区分。

2、线程:又称轻量级进程,是进程中的一条执行路径,也是CPU的基本调度单位,一个进程由一个或多个线程组成,彼此间完成不同的工作,同时执行,称为多线程。

1、一个进程可以包含多个线程,但至少得有一个线程;

2、进程间不能共享数据段地址,但同进程的线程之间可以。

3、线程的组成:

任何一个线程都具有基本的组成部分:

(1)CPU时间片:操作系统(OS)会为每个线程分配执行时间;

(2)运行数据:堆空间:存储线程需使用的对象,多个线程可以共享堆中的对象;

栈空间:存储线程需使用的局部变量,每个线程都拥有独立的栈。

4、线程的特点:

(1)线程抢占性执行,效率高,可防止单一线程长时间占用CPU;

(2)在单核CPU中,宏观上同时执行,微观上顺序执行。

二、创建线程

1、方式:

(1)继承Thread类,重写run方法;

(2)实现Runnable接口;

(3)实现Callable接口;

(1)继承Thread类

复制代码
 package com.qf.chapter_07;

    
  
    
 public class MyThread  extends Thread{
    
 	public MyThread() {
    
 		// TODO 自动生成的构造函数存根
    
 	}
    
 	public MyThread(String name) {
    
 		super(name);//构造函数,来实现修改线程名称
    
 	}
    
 	
    
 	
    
 	@Override
    
 	public void run() {
    
 		for(int i=0;i<200;i++) {
    
 			//方法1,this.getId,this.getName,前提是继承了Thread
    
 			//System.out.println("线程ID:"+this.getId()+"线程名称"+this.getName()+"子线程--------"+i);
    
 		    //方法2,Thread.currentThread()获取当前线程,可不继承Thread
    
 			System.out.println("ID:"+Thread.currentThread().getId()+"名称:"+Thread.currentThread().getName()+"子线程-----"+i);
    
 		}
    
 	}
    
 }
    
    
    
    
复制代码
 package com.qf.chapter_07;

    
  
    
 public class TestThread {
    
 	public static void main(String[] args) {
    
 		//1.创建线程对象
    
 		MyThread myThread=new MyThread("我的子线程1");//方法2,构造函数
    
 		//2.启动线程,调用start方法,谁抢到子线程谁执行
    
 		//修改线程名称,方法1,在启动前修改,线程Id不能改
    
 		//myThread.setName("我的主线程1");
    
 		
    
 		myThread.start();
    
 		MyThread myThread2=new MyThread();
    
 		myThread2.setName("我的子线程2");
    
 		myThread2.start();
    
 		for(int i=0;i<100;i++) {
    
 			System.out.println("主线程------"+i);
    
 		}
    
 	}
    
 }
    
    
    
    
复制代码
 package com.qf.chapter_07;

    
  
    
 public class TicketWin extends Thread{
    
 	public TicketWin() {
    
 		// TODO 自动生成的构造函数存根
    
 	}
    
 	
    
 	public TicketWin(int ticket) {
    
 		super();
    
 		this.ticket = ticket;
    
 	}
    
 	public TicketWin(String name) {
    
 		super(name);
    
 	}
    
  
    
 	private int ticket=100;
    
 	@Override
    
 	public void run() {
    
 		while(true) {
    
 			if(ticket<=0) {
    
 				break;
    
 			}
    
 			System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");
    
 			ticket--;
    
 		}
    
 	}
    
 }
    
    
    
    
复制代码
 package com.qf.chapter_07;

    
  
    
 public class TestTicket {
    
 	public static void main(String[] args) {
    
 		//创建窗口
    
 		TicketWin  w1=new TicketWin("窗口1");
    
 		TicketWin  w2=new TicketWin("窗口2");
    
 		TicketWin  w3=new TicketWin("窗口3");
    
 		TicketWin  w4=new TicketWin("窗口4");
    
 		//启动
    
 		w1.start();
    
 		w2.start();
    
 		w3.start();
    
 		w4.start();
    
 		
    
 	}
    
 }
    
    
    
    

(2)实现Runnable接口---常用

模拟抢票

复制代码
 package com.java.main;

    
  
    
 public class Main implements Runnable
    
 {
    
 	private int ticketNum=10;
    
 	public void run()
    
 	{
    
 		while(true)
    
 		{
    
 			if(ticketNum<=0)break;
    
 			try 
    
 			{
    
 				Thread.sleep(200);// 模拟延时,需要try-catch捕获异常
    
 			} catch (InterruptedException e)
    
 			{
    
 				// TODO 自动生成的 catch 块
    
 				e.printStackTrace();
    
 			}
    
 			// Thread.currentThread().getName() 方法得到当前运行的线程名称
    
 			System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--+"张票");
    
 		}
    
 	}
    
 	public static void main(String[] args) 
    
 	{
    
 		// 创建实现了Runnable 接口的类的对象
    
 		Main ticket=new Main();
    
 		//开启多个线程
    
 		new Thread(ticket,"小明").start();
    
 		new Thread(ticket,"小刚老师").start();
    
 		new Thread(ticket,"黄牛佬").start();
    
 	}
    
 	
    
 	
    
 }
    
    
    
    
复制代码
 package com.qf.chapter_07;

    
  
    
 public class MyRunnable implements Runnable {
    
 	@Override
    
 	public void run() {
    
 		for(int i=0;i<10;i++) {
    
 			System.out.println(Thread.currentThread().getName()+"------"+i);
    
 		}
    
 		
    
 	}
    
 }
    
    
    
    
复制代码
 package com.qf.chapter_07;

    
  
    
 public class TestRunnable {
    
 	public static void main(String[] args) {
    
 		/*第一种方法,继承Runnable接口
    
 		//1.创建Runnable对象,表示线程要执行的功能
    
 		MyRunnable runnable=new MyRunnable();
    
 		//2.创建线程对象
    
 		Thread thread=new Thread(runnable,"我的线程1");
    
 		//3启动
    
 		thread.start();
    
 		
    
 		//主线程执行
    
 		for(int i=0;i<20;i++) {
    
 			System.out.println("main----"+i);
    
 		}
    
 		*/
    
 		
    
 		//第二种方法,使用匿名内部类
    
 		Runnable runnable=new Runnable() {
    
 			
    
 			@Override
    
 			public void run() {
    
 				// TODO 自动生成的方法存根
    
 				for(int i=0;i<10;i++) {
    
 					System.out.println(Thread.currentThread().getName()+"------"+i);
    
 				}
    
 			}
    
 		};
    
 		//创建线程对象
    
 		Thread thread=new Thread(runnable,"我的线程1");
    
 		//启动线程
    
 		thread.start();
    
 	}
    
 }
    
    
    
    

(3)实现Callable接口--了解

复制代码
 public class CallableThreadTest implements Callable<Integer> {

    
     public static void main(String[] args)  {  
    
     CallableThreadTest ctt = new CallableThreadTest();  
    
     FutureTask<Integer> ft = new FutureTask<>(ctt);  
    
     for(int i = 0;i < 100;i++)  {  
    
         System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i); 
    
         if(i==20)  {  
    
             new Thread(ft,"有返回值的线程").start();  
    
         }  
    
     }  
    
     try  {  
    
         System.out.println("子线程的返回值:"+ft.get());  
    
     } catch (InterruptedException e)  {  
    
         e.printStackTrace();  
    
     } catch (ExecutionException e)  {  
    
         e.printStackTrace();  
    
     }  
    
     }
    
     @Override  
    
     public Integer call() throws Exception  {  
    
     int i = 0;  
    
     for(;i<100;i++)  {  
    
         System.out.println(Thread.currentThread().getName()+" "+i);  
    
     }  
    
     return i;  
    
     }  
    
 }
    
    
    
    

三、线程的状态

1、线程休眠:public static void sleep(long millis); 单位毫秒

复制代码
 package com.qf.chapter_09;

    
  
    
 public class SleepThread extends Thread {
    
 	@Override
    
 	public void run() {
    
 		for(int i=0;i<10;i++) {
    
 			System.out.println(Thread.currentThread().getName()+"-----"+i);
    
 			try {
    
 				Thread.sleep(1000);
    
 			} catch (Exception e) {
    
 				// TODO: handle exception
    
 				e.printStackTrace();
    
 			}
    
 		}
    
 	}
    
  
    
 	
    
 }
    
    
    
    
复制代码
 package com.qf.chapter_09;

    
  
    
 public class TestSleep {
    
 	public static void main(String[] args) {
    
 		SleepThread s1=new SleepThread();
    
 		s1.start();
    
 		SleepThread s2=new SleepThread();
    
 		s2.start();
    
 	}
    
 }
    
    
    
    

2、放弃:public static void yield();

当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片。实现多个线程交替执行。

复制代码
 package com.java.main;

    
  
    
 public class TestYield
    
 {
    
 	public static void main(String[] args)
    
 	{
    
 		TestY testY=new TestY();
    
 		new Thread(testY,"A").start();
    
 		new Thread(testY,"B").start();
    
 	}
    
 	
    
 }
    
  
    
 class TestY implements Runnable 
    
 {
    
 	@Override
    
 	public void run()
    
 	{
    
 		System.out.println(Thread.currentThread().getName()+"线程启动");
    
 		Thread.yield();
    
 		System.out.println(Thread.currentThread().getName()+"线程终止");
    
 	}
    
 }
    
    
    
    

3、加入;public final void join();

允许其他线程加入到当前线程中。

复制代码
 package com.qf.chapter_09;

    
  
    
 public class JoinThread extends Thread {
    
 	@Override
    
 	public void run() {
    
 		for(int i=0;i<30;i++) {
    
 			System.out.println(Thread.currentThread().getName()+"-----"+i);
    
 			try {
    
 				Thread.sleep(500);
    
 			} catch (InterruptedException e) {
    
 				// TODO 自动生成的 catch 块
    
 				e.printStackTrace();
    
 			}
    
 		}
    
 	}
    
 }
    
    
    
    
复制代码
 package com.qf.chapter_09;

    
  
    
 public class TestJoin {
    
 	public static void main(String[] args) {
    
 		JoinThread j1=new JoinThread();
    
 		j1.start();
    
 		//加入当前线程(主线程),并阻塞当前线程,直到加入线程执行完毕。
    
 		try {
    
 			j1.join();//先执行完毕子线程,再执行主线程
    
 		} catch (InterruptedException e1) {
    
 			// TODO 自动生成的 catch 块
    
 			e1.printStackTrace();
    
 		}
    
 		for(int i=0;i<20;i++) {
    
 			System.out.println(Thread.currentThread().getName()+"---"+i);
    
 			try {
    
 				Thread.sleep(100);
    
 			} catch (InterruptedException e) {
    
 				// TODO 自动生成的 catch 块
    
 				e.printStackTrace();
    
 			}
    
 		}
    
 	}
    
 }
    
    
    
    

4、设置线程优先级

(1)线程对象.setPriority();

(2)线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多;

复制代码
 public class TestYield

    
 {
    
 	public static void main(String[] args)
    
 	{
    
 		TestY testY=new TestY();
    
 		
    
 		Thread t1=new Thread(testY);
    
 		Thread t2=new Thread(testY);
    
 		Thread t3=new Thread(testY);
    
 		Thread t4=new Thread(testY);
    
 		 
    
 		// t1默认优先级是5
    
 		t1.start();
    
 		// t2优先级设置为最大
    
 		t2.setPriority(Thread.MAX_PRIORITY); //  Thread.MAX_PRIORITY==10 优先级最高
    
 		t2.start();
    
 		// t3优先级设置为最小
    
 		t3.setPriority(Thread.MIN_PRIORITY); // Thread.MIN_PRIORITY==1 优先级最低
    
 		t3.start();
    
 		// t4优先级设为 3
    
 		t4.setPriority(3);
    
 		t4.start();
    
 	}
    
 	
    
 }
    
  
    
  
    
 class TestY implements Runnable 
    
 {
    
 	@Override
    
 	public void run()
    
 	{
    
 		System.out.println(Thread.currentThread().getName()+"优先级是"+Thread.currentThread().getPriority());
    
 		
    
 	}
    
 }
    
    
    
    

(3)守护线程:

线程对象.setDaemon(true);设置为守护线程;

线程有两类:用户线程(前台线程),守护线程(后台线程)

如果程序中所有前台程序都执行完毕,后台程序会自动结束;

垃圾回收器线程属于守护线程。

复制代码
 package com.qf.chapter_09;

    
  
    
 public class PriorityThread extends Thread{
    
 	@Override
    
 	public void run() {
    
 		for(int i=0;i<50;i++) {
    
 			System.out.println(Thread.currentThread().getName()+"---"+i);
    
 		}
    
 	}
    
 }
    
    
    
    
复制代码
 package com.qf.chapter_09;

    
  
    
 public class TestPriority {
    
 	public static void main(String[] args) {
    
 		
    
 		PriorityThread p1=new PriorityThread();
    
 		p1.setName("p1");
    
 		PriorityThread p2=new PriorityThread();
    
 		p2.setName("p2");
    
 		PriorityThread p3=new PriorityThread();
    
 		p3.setName("p3");
    
 		p1.setPriority(1);//优先级设为1,优先级越高越先执行,但是不是绝对的
    
 		p3.setPriority(10);//设为10
    
 		//
    
 		p1.start();
    
 		//设置守护线程,再调用start之前设置
    
 		p2.setDaemon(true);
    
 		p2.start();
    
 		p3.start();
    
 		//
    
 		
    
 	}
    
 }
    
    
    
    

5、线程安全问题:

当多线程并发访问临界资源时,如果破坏原子操作(不可分割的多步操作),可能会造成数据不一致;

临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其准确性。

(1)同步方法

复制代码
 package com.java.main;

    
  
    
 public class TestSync
    
 {
    
 	public static void main(String[] args)
    
 	{
    
 		BuyTicket station = new BuyTicket();
    
 		
    
 		new Thread(station,"小明").start();
    
 		new Thread(station,"小刚老师").start();
    
 		new Thread(station,"黄牛佬").start();
    
 	}
    
 }
    
 class BuyTicket implements Runnable
    
 {
    
 	private int ticketNum=10;
    
 	boolean flag=true;
    
 	public void run()
    
 	{
    
 		while(true)
    
 		{
    
 			if(!flag)break;
    
 			buy();
    
 			try
    
 			{
    
 				Thread.sleep(200);
    
 			} catch (InterruptedException e)
    
 			{
    
 				// TODO 自动生成的 catch 块
    
 				e.printStackTrace();
    
 			}
    
 		}
    
 	}
    
 // 在不安全的方法加上 synchronize 修饰
    
 	private synchronized void buy()
    
 	{
    
 		if(ticketNum<=0)
    
 		{
    
 			flag=false;
    
 			return;
    
 		}
    
 		System.out.println(Thread.currentThread().getName()+"买到了第"+ticketNum--+"张票");
    
 	}
    
 }
    
    
    
    

以上是模拟抢票程序,如果buy方法不加synchronize修饰,那么运行结果可能出现有人抢到第0张票或者负数张票,加上同步锁之后就不会。

(2)同步代码块

synchronized(临界资源对象){//对临界资源对象加锁

//代码(原子操作)

}

每一个对象都有一个互斥锁标记,用来分配给线程的;

只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块;

线程退出同步代码块时,会释放相应的互斥锁标记。

复制代码
 package com.java.main;

    
  
    
 import java.util.List;
    
 import java.util.ArrayList;
    
  
    
 public class TestList
    
 {
    
 	public static void main(String[] args)
    
 	{
    
 		List<String> list =new ArrayList<String>();
    
 		for(int i=1;i<=10000;i++)
    
 		{
    
 			new Thread(()->
    
 			{	
    
 				synchronized (list)
    
 				{
    
 					list.add(Thread.currentThread().getName());
    
 				}
    
 	
    
 			}).start();
    
 		}
    
 		System.out.println(list.size());
    
 	}
    
 }
    
    
    
    

(3)同步规则:

只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记;

已知JDK中线程安全的类:

StringBuffer、Vector、Hashtable

6、线程通信:

等待:

public final void wait()

public final void wait(long timeout)

必须在对obj加锁的同步代码块中,在一个线程中,调用obj.wait()时,此线程会释放其拥有的所有锁标记,同时此线程阻塞在o的等待队列中。释放锁,进入等待序列。

通知:

public final void notify();

public final void notifyAll()

全部评论 (0)

还没有任何评论哟~